@rshono/core 1.0.0-rc.2 → 1.0.0-rc.21

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 (208) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +348 -158
  3. package/bin/rshono.mjs +3 -4
  4. package/dist/builder/env-shadow-loader.cjs +123 -8
  5. package/dist/builder/page-entry-loader.cjs +28 -1
  6. package/dist/builder/page-files.d.ts +0 -1
  7. package/dist/builder/page-files.js +7 -3
  8. package/dist/builder/page-files.js.map +1 -1
  9. package/dist/builder/public-env.d.ts +7 -1
  10. package/dist/builder/public-env.js +7 -0
  11. package/dist/builder/public-env.js.map +1 -1
  12. package/dist/builder/react-versions.d.ts +12 -0
  13. package/dist/builder/react-versions.js +74 -0
  14. package/dist/builder/react-versions.js.map +1 -0
  15. package/dist/builder/rspack-config.d.ts +13 -4
  16. package/dist/builder/rspack-config.js +136 -53
  17. package/dist/builder/rspack-config.js.map +1 -1
  18. package/dist/builder/server-only-imports.d.ts +32 -0
  19. package/dist/builder/server-only-imports.js +51 -0
  20. package/dist/builder/server-only-imports.js.map +1 -0
  21. package/dist/cli/build.d.ts +2 -3
  22. package/dist/cli/build.js +51 -13
  23. package/dist/cli/build.js.map +1 -1
  24. package/dist/cli/dev.d.ts +2 -3
  25. package/dist/cli/dev.js +149 -45
  26. package/dist/cli/dev.js.map +1 -1
  27. package/dist/cli/exit.d.ts +12 -0
  28. package/dist/cli/exit.js +16 -0
  29. package/dist/cli/exit.js.map +1 -0
  30. package/dist/cli/index.d.ts +0 -1
  31. package/dist/cli/index.js +91 -30
  32. package/dist/cli/index.js.map +1 -1
  33. package/dist/cli/start.d.ts +0 -1
  34. package/dist/cli/start.js +15 -18
  35. package/dist/cli/start.js.map +1 -1
  36. package/dist/config.d.ts +72 -78
  37. package/dist/config.js +17 -1
  38. package/dist/config.js.map +1 -1
  39. package/dist/deploy/aws-lambda/runtime.d.ts +4 -7
  40. package/dist/deploy/aws-lambda/runtime.js +5 -8
  41. package/dist/deploy/aws-lambda/runtime.js.map +1 -1
  42. package/dist/deploy/build-marker.d.ts +9 -6
  43. package/dist/deploy/build-marker.js +8 -4
  44. package/dist/deploy/build-marker.js.map +1 -1
  45. package/dist/deploy/cloudflare/build.d.ts +0 -1
  46. package/dist/deploy/cloudflare/build.js +17 -13
  47. package/dist/deploy/cloudflare/build.js.map +1 -1
  48. package/dist/deploy/cloudflare/runtime.d.ts +2 -6
  49. package/dist/deploy/cloudflare/runtime.js +73 -68
  50. package/dist/deploy/cloudflare/runtime.js.map +1 -1
  51. package/dist/deploy/contract.d.ts +57 -41
  52. package/dist/deploy/contract.js.map +1 -1
  53. package/dist/deploy/filesystem.d.ts +3 -6
  54. package/dist/deploy/filesystem.js +23 -20
  55. package/dist/deploy/filesystem.js.map +1 -1
  56. package/dist/deploy/node/runtime.d.ts +5 -3
  57. package/dist/deploy/node/runtime.js +23 -9
  58. package/dist/deploy/node/runtime.js.map +1 -1
  59. package/dist/deploy/presets.d.ts +34 -30
  60. package/dist/deploy/presets.js +56 -44
  61. package/dist/deploy/presets.js.map +1 -1
  62. package/dist/deploy/public-paths.d.ts +42 -0
  63. package/dist/deploy/public-paths.js +66 -0
  64. package/dist/deploy/public-paths.js.map +1 -0
  65. package/dist/deploy/vercel/build.d.ts +0 -1
  66. package/dist/deploy/vercel/build.js +25 -16
  67. package/dist/deploy/vercel/build.js.map +1 -1
  68. package/dist/deploy/vercel/runtime.d.ts +10 -7
  69. package/dist/deploy/vercel/runtime.js +48 -11
  70. package/dist/deploy/vercel/runtime.js.map +1 -1
  71. package/dist/index.d.ts +19 -17
  72. package/dist/index.js +20 -10
  73. package/dist/index.js.map +1 -1
  74. package/dist/router.d.ts +212 -120
  75. package/dist/router.js +3 -8
  76. package/dist/router.js.map +1 -1
  77. package/dist/runtime/boundaries.d.ts +62 -48
  78. package/dist/runtime/boundaries.js +32 -34
  79. package/dist/runtime/boundaries.js.map +1 -1
  80. package/dist/runtime/client.d.ts +20 -11
  81. package/dist/runtime/client.js +20 -10
  82. package/dist/runtime/client.js.map +1 -1
  83. package/dist/runtime/context.d.ts +304 -123
  84. package/dist/runtime/context.js +377 -149
  85. package/dist/runtime/context.js.map +1 -1
  86. package/dist/runtime/control.d.ts +15 -1
  87. package/dist/runtime/control.js +24 -0
  88. package/dist/runtime/control.js.map +1 -1
  89. package/dist/runtime/dev-protocol.d.ts +4 -9
  90. package/dist/runtime/dev-protocol.js.map +1 -1
  91. package/dist/runtime/empty-server-app.d.ts +0 -1
  92. package/dist/runtime/entry.client.d.ts +4 -1
  93. package/dist/runtime/entry.client.js +455 -313
  94. package/dist/runtime/entry.client.js.map +1 -1
  95. package/dist/runtime/entry.rsc.d.ts +15 -7
  96. package/dist/runtime/entry.rsc.js +811 -252
  97. package/dist/runtime/entry.rsc.js.map +1 -1
  98. package/dist/runtime/entry.ssr.d.ts +27 -10
  99. package/dist/runtime/entry.ssr.js +30 -48
  100. package/dist/runtime/entry.ssr.js.map +1 -1
  101. package/dist/runtime/failure-document.d.ts +11 -0
  102. package/dist/runtime/failure-document.js +35 -0
  103. package/dist/runtime/failure-document.js.map +1 -0
  104. package/dist/runtime/flight-inject.d.ts +23 -0
  105. package/dist/runtime/flight-inject.js +403 -0
  106. package/dist/runtime/flight-inject.js.map +1 -0
  107. package/dist/runtime/hot-update.d.ts +44 -0
  108. package/dist/runtime/hot-update.js +44 -0
  109. package/dist/runtime/hot-update.js.map +1 -0
  110. package/dist/runtime/navigation.d.ts +46 -54
  111. package/dist/runtime/navigation.js +22 -63
  112. package/dist/runtime/navigation.js.map +1 -1
  113. package/dist/runtime/request.d.ts +42 -8
  114. package/dist/runtime/request.js +84 -7
  115. package/dist/runtime/request.js.map +1 -1
  116. package/dist/runtime/server.d.ts +20 -18
  117. package/dist/runtime/server.js +22 -17
  118. package/dist/runtime/server.js.map +1 -1
  119. package/dist/runtime/validate-entries.d.ts +77 -0
  120. package/dist/runtime/validate-entries.js +405 -0
  121. package/dist/runtime/validate-entries.js.map +1 -0
  122. package/dist/server/headers.d.ts +41 -15
  123. package/dist/server/headers.js +53 -24
  124. package/dist/server/headers.js.map +1 -1
  125. package/dist/server/load-config.d.ts +4 -5
  126. package/dist/server/load-config.js +22 -14
  127. package/dist/server/load-config.js.map +1 -1
  128. package/dist/server/load-env.d.ts +0 -1
  129. package/dist/server/prerendered.d.ts +88 -34
  130. package/dist/server/prerendered.js +184 -24
  131. package/dist/server/prerendered.js.map +1 -1
  132. package/dist/server/server-config.d.ts +35 -50
  133. package/dist/server/server-config.js +27 -74
  134. package/dist/server/server-config.js.map +1 -1
  135. package/dist/server/shutdown.d.ts +5 -4
  136. package/dist/server/shutdown.js +5 -3
  137. package/dist/server/shutdown.js.map +1 -1
  138. package/dist/server/ssg.d.ts +9 -7
  139. package/dist/server/ssg.js +209 -65
  140. package/dist/server/ssg.js.map +1 -1
  141. package/dist/server/static.d.ts +0 -1
  142. package/dist/server/static.js +7 -1
  143. package/dist/server/static.js.map +1 -1
  144. package/package.json +21 -23
  145. package/dist/builder/page-files.d.ts.map +0 -1
  146. package/dist/builder/public-env.d.ts.map +0 -1
  147. package/dist/builder/rspack-config.d.ts.map +0 -1
  148. package/dist/cli/build.d.ts.map +0 -1
  149. package/dist/cli/dev.d.ts.map +0 -1
  150. package/dist/cli/index.d.ts.map +0 -1
  151. package/dist/cli/start.d.ts.map +0 -1
  152. package/dist/config.d.ts.map +0 -1
  153. package/dist/deploy/aws-lambda/runtime.d.ts.map +0 -1
  154. package/dist/deploy/build-marker.d.ts.map +0 -1
  155. package/dist/deploy/bun/runtime.d.ts +0 -11
  156. package/dist/deploy/bun/runtime.d.ts.map +0 -1
  157. package/dist/deploy/bun/runtime.js +0 -22
  158. package/dist/deploy/bun/runtime.js.map +0 -1
  159. package/dist/deploy/cloudflare/build.d.ts.map +0 -1
  160. package/dist/deploy/cloudflare/runtime.d.ts.map +0 -1
  161. package/dist/deploy/contract.d.ts.map +0 -1
  162. package/dist/deploy/deno/runtime.d.ts +0 -11
  163. package/dist/deploy/deno/runtime.d.ts.map +0 -1
  164. package/dist/deploy/deno/runtime.js +0 -16
  165. package/dist/deploy/deno/runtime.js.map +0 -1
  166. package/dist/deploy/filesystem.d.ts.map +0 -1
  167. package/dist/deploy/listen.d.ts +0 -20
  168. package/dist/deploy/listen.d.ts.map +0 -1
  169. package/dist/deploy/listen.js +0 -24
  170. package/dist/deploy/listen.js.map +0 -1
  171. package/dist/deploy/netlify/build.d.ts +0 -8
  172. package/dist/deploy/netlify/build.d.ts.map +0 -1
  173. package/dist/deploy/netlify/build.js +0 -52
  174. package/dist/deploy/netlify/build.js.map +0 -1
  175. package/dist/deploy/netlify/runtime.d.ts +0 -13
  176. package/dist/deploy/netlify/runtime.d.ts.map +0 -1
  177. package/dist/deploy/netlify/runtime.js +0 -24
  178. package/dist/deploy/netlify/runtime.js.map +0 -1
  179. package/dist/deploy/node/runtime.d.ts.map +0 -1
  180. package/dist/deploy/presets.d.ts.map +0 -1
  181. package/dist/deploy/vercel/build.d.ts.map +0 -1
  182. package/dist/deploy/vercel/runtime.d.ts.map +0 -1
  183. package/dist/index.d.ts.map +0 -1
  184. package/dist/router.d.ts.map +0 -1
  185. package/dist/runtime/boundaries.d.ts.map +0 -1
  186. package/dist/runtime/client.d.ts.map +0 -1
  187. package/dist/runtime/context.d.ts.map +0 -1
  188. package/dist/runtime/control.d.ts.map +0 -1
  189. package/dist/runtime/dev-protocol.d.ts.map +0 -1
  190. package/dist/runtime/empty-server-app.d.ts.map +0 -1
  191. package/dist/runtime/entry.client.d.ts.map +0 -1
  192. package/dist/runtime/entry.rsc.d.ts.map +0 -1
  193. package/dist/runtime/entry.ssr.d.ts.map +0 -1
  194. package/dist/runtime/navigation.d.ts.map +0 -1
  195. package/dist/runtime/request.d.ts.map +0 -1
  196. package/dist/runtime/server.d.ts.map +0 -1
  197. package/dist/server/compress.d.ts +0 -15
  198. package/dist/server/compress.d.ts.map +0 -1
  199. package/dist/server/compress.js +0 -76
  200. package/dist/server/compress.js.map +0 -1
  201. package/dist/server/headers.d.ts.map +0 -1
  202. package/dist/server/load-config.d.ts.map +0 -1
  203. package/dist/server/load-env.d.ts.map +0 -1
  204. package/dist/server/prerendered.d.ts.map +0 -1
  205. package/dist/server/server-config.d.ts.map +0 -1
  206. package/dist/server/shutdown.d.ts.map +0 -1
  207. package/dist/server/ssg.d.ts.map +0 -1
  208. package/dist/server/static.d.ts.map +0 -1
@@ -1,102 +1,116 @@
1
1
  import { Component, type ReactNode } from 'react';
2
2
  /**
3
- * What an {@link ErrorBoundary} / {@link Boundary} renders once a child throws.
4
- * Either a static node, or a render function that also gets a `reset` callback
5
- * to clear the error and re-render the children (e.g. a "Try again" button).
3
+ * What a {@link CatchBoundary} / {@link AsyncBoundary} renders once a child throws: either a static
4
+ * node, or a render function given the error and a `reset` callback that clears it and re-renders the
5
+ * children (a "Try again" button, say).
6
6
  *
7
- * The render-function form only works when the boundary is used from a `'use
8
- * client'` component — functions can't cross the server→client boundary. From a
9
- * server component, pass a `ReactNode`.
7
+ * The function form only works from a `'use client'` component functions can't cross the
8
+ * server→client boundary. From a server component, pass a `ReactNode`.
10
9
  */
11
10
  export type ErrorFallback = ReactNode | ((error: Error, reset: () => void) => ReactNode);
12
- export interface ErrorBoundaryProps {
11
+ /** Props for {@link CatchBoundary}. */
12
+ export interface CatchBoundaryProps {
13
13
  /**
14
- * Rendered in place of the children after one of them throws. Omit it to
15
- * report the error via `onError` and re-throw to the next boundary out (or
16
- * the global error page) instead of handling it here.
14
+ * Rendered in place of the children after one of them throws. Omit it to report the error via
15
+ * `onError` and re-throw to the next boundary out or the app's `error` page — instead of handling it
16
+ * here.
17
17
  */
18
18
  fallback?: ErrorFallback;
19
- /** Called with the caught error (for logging / reporting). */
19
+ /**
20
+ * Called with the caught error, for logging or reporting.
21
+ *
22
+ * A function prop, so — like {@link ErrorFallback}'s function form — it can only be passed from a
23
+ * `'use client'` component. React refuses one from a server component by name: "Event handlers cannot be
24
+ * passed to Client Component props".
25
+ */
20
26
  onError?: (error: Error) => void;
21
27
  /**
22
- * When any value in this array changes while the boundary is showing its
23
- * fallback, the error is cleared automatically. Pass the current pathname to
24
- * recover when the user navigates away: `resetKeys={[useNavigation().url.pathname]}`.
28
+ * Clears the error automatically when any value in this array changes while the fallback is showing.
29
+ * Pass the current pathname to recover when the user navigates away — `resetKeys={[url.pathname]}` from a
30
+ * page's `url` prop, which is the form that works from the server component rendering this boundary, or
31
+ * `resetKeys={[useNavigation().url.pathname]}` inside a `'use client'` component.
25
32
  */
26
33
  resetKeys?: readonly unknown[];
34
+ /** The subtree this boundary protects. */
27
35
  children: ReactNode;
28
36
  }
29
- interface ErrorBoundaryState {
37
+ interface CatchBoundaryState {
30
38
  error: Error | null;
31
39
  }
32
40
  /**
33
- * A general-purpose error boundary. Catches errors thrown while rendering its
34
- * children a client island that blew up, or a server component that rejected
35
- * on a soft navigation — and renders `fallback` in their place instead of
36
- * tearing down the whole page.
41
+ * A general-purpose error boundary: catches what its children throw — a client island that blew up, a
42
+ * server component that rejected on a soft navigation and renders `fallback` in their place rather
43
+ * than tearing down the page.
37
44
  *
38
- * It's a `'use client'` component (React error boundaries must be), so drop it
39
- * anywhere in the tree from a server or client component. Use {@link Boundary}
40
- * when you also want a Suspense loading fallback in the same wrapper.
45
+ * It is a `'use client'` component (React error boundaries must be), so a server component can render
46
+ * it too. Reach for {@link AsyncBoundary} when you also want a Suspense loading fallback.
41
47
  *
42
48
  * @example
43
49
  * ```tsx
44
- * import { ErrorBoundary } from '@rshono/core/client';
50
+ * import { CatchBoundary } from '@rshono/core/client';
45
51
  *
46
- * <ErrorBoundary fallback={(error, reset) => (
52
+ * <CatchBoundary fallback={(error, reset) => (
47
53
  * <div role="alert">
48
54
  * <p>{error.message}</p>
49
55
  * <button onClick={reset}>Try again</button>
50
56
  * </div>
51
57
  * )}>
52
58
  * <RiskyWidget />
53
- * </ErrorBoundary>
59
+ * </CatchBoundary>
54
60
  * ```
61
+ *
62
+ * @see {@link https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary | React — error boundaries}
63
+ * @see {@link https://www.rshono.com/docs/api#rshonocoreclient | Docs — `@rshono/core/client`}
55
64
  */
56
- export declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
57
- state: ErrorBoundaryState;
58
- static getDerivedStateFromError(error: Error): ErrorBoundaryState;
65
+ export declare class CatchBoundary extends Component<CatchBoundaryProps, CatchBoundaryState> {
66
+ state: CatchBoundaryState;
67
+ static getDerivedStateFromError(error: Error): CatchBoundaryState;
59
68
  componentDidCatch(error: Error): void;
60
- componentDidUpdate(prev: ErrorBoundaryProps): void;
69
+ componentDidUpdate(prev: CatchBoundaryProps): void;
61
70
  reset: () => void;
62
71
  render(): ReactNode;
63
72
  }
64
- export interface BoundaryProps {
65
- /** Suspense fallback, shown while the children (or their data) are still loading. */
66
- loading?: ReactNode;
73
+ /** Props for {@link AsyncBoundary}. */
74
+ export interface AsyncBoundaryProps {
75
+ /**
76
+ * Suspense fallback, shown while the children or their data are still loading. Required — a loading
77
+ * state is the reason to reach for this over {@link CatchBoundary}, so showing nothing is an explicit
78
+ * `loading={null}`.
79
+ */
80
+ loading: ReactNode;
67
81
  /** Error fallback, shown if a child throws. See {@link ErrorFallback}. */
68
82
  error?: ErrorFallback;
69
- /** Called with the caught error. */
83
+ /** Called with the caught error. From a `'use client'` component only — see {@link CatchBoundaryProps.onError}. */
70
84
  onError?: (error: Error) => void;
71
- /** Clears the error fallback when any value changes — see {@link ErrorBoundaryProps.resetKeys}. */
85
+ /** Clears the error fallback when any value changes — see {@link CatchBoundaryProps.resetKeys}. */
72
86
  resetKeys?: readonly unknown[];
87
+ /** The subtree this boundary suspends on and protects. */
73
88
  children: ReactNode;
74
89
  }
75
90
  /**
76
- * A loading + error boundary in one wrapper — the common case for an async
77
- * section of a page. It always renders the same shape:
91
+ * A loading and error boundary in one wrapper — the common case for an async section of a page. It
92
+ * always renders the same shape:
78
93
  *
79
94
  * ```tsx
80
- * <ErrorBoundary fallback={error}>
95
+ * <CatchBoundary fallback={error}>
81
96
  * <Suspense fallback={loading}>{children}</Suspense>
82
- * </ErrorBoundary>
97
+ * </CatchBoundary>
83
98
  * ```
84
99
  *
85
- * so `error` catches anything the children throw (including while suspended) and
86
- * `loading` shows until they resolve. Both fallbacks are optional: omit
87
- * `loading` and nothing shows while loading; omit `error` and thrown errors
88
- * propagate to the next boundary out (or the global error page) rather than
89
- * being caught here.
100
+ * so `loading` shows until the children resolve and `error` catches whatever they throw, suspended or
101
+ * not. `error` is optional: omit it and errors propagate to the next boundary out.
90
102
  *
91
103
  * @example
92
104
  * ```tsx
93
- * import { Boundary } from '@rshono/core/client';
105
+ * import { AsyncBoundary } from '@rshono/core/client';
94
106
  *
95
- * <Boundary loading={<Spinner />} error={(e, reset) => <Retry onClick={reset} />}>
107
+ * <AsyncBoundary loading={<Spinner />} error={(e, reset) => <Retry onClick={reset} />}>
96
108
  * <SlowServerComponent />
97
- * </Boundary>
109
+ * </AsyncBoundary>
98
110
  * ```
111
+ *
112
+ * @see {@link https://react.dev/reference/react/Suspense | React — `<Suspense>`}
113
+ * @see {@link https://www.rshono.com/docs/api#rshonocoreclient | Docs — `@rshono/core/client`}
99
114
  */
100
- export declare function Boundary({ loading, error, onError, resetKeys, children }: BoundaryProps): ReactNode;
115
+ export declare function AsyncBoundary({ loading, error, onError, resetKeys, children }: AsyncBoundaryProps): ReactNode;
101
116
  export {};
102
- //# sourceMappingURL=boundaries.d.ts.map
@@ -2,12 +2,9 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Component, Suspense } from 'react';
4
4
  import { isControlDigest } from './control.js';
5
- /**
6
- * `redirect()` and `notFound()` reach the browser as a thrown error carrying a control digest.
7
- * They are navigation, not failure, so no boundary may absorb one — otherwise a `redirect()` from
8
- * a component inside a `<Boundary>` would render "something went wrong" instead of navigating.
9
- * They're re-thrown to the root, where the runtime turns the digest into a real navigation.
10
- */
5
+ // `redirect()` and `notFound()` reach the browser as a thrown error carrying a control digest. They are
6
+ // navigation, not failure, so no boundary absorbs one they are re-thrown to the root, where the
7
+ // runtime turns the digest into a real navigation.
11
8
  function isControlError(error) {
12
9
  return isControlDigest(error?.digest);
13
10
  }
@@ -15,37 +12,38 @@ function keysChanged(a, b) {
15
12
  return a.length !== b.length || a.some((value, i) => !Object.is(value, b[i]));
16
13
  }
17
14
  /**
18
- * A general-purpose error boundary. Catches errors thrown while rendering its
19
- * children a client island that blew up, or a server component that rejected
20
- * on a soft navigation — and renders `fallback` in their place instead of
21
- * tearing down the whole page.
15
+ * A general-purpose error boundary: catches what its children throw — a client island that blew up, a
16
+ * server component that rejected on a soft navigation and renders `fallback` in their place rather
17
+ * than tearing down the page.
22
18
  *
23
- * It's a `'use client'` component (React error boundaries must be), so drop it
24
- * anywhere in the tree from a server or client component. Use {@link Boundary}
25
- * when you also want a Suspense loading fallback in the same wrapper.
19
+ * It is a `'use client'` component (React error boundaries must be), so a server component can render
20
+ * it too. Reach for {@link AsyncBoundary} when you also want a Suspense loading fallback.
26
21
  *
27
22
  * @example
28
23
  * ```tsx
29
- * import { ErrorBoundary } from '@rshono/core/client';
24
+ * import { CatchBoundary } from '@rshono/core/client';
30
25
  *
31
- * <ErrorBoundary fallback={(error, reset) => (
26
+ * <CatchBoundary fallback={(error, reset) => (
32
27
  * <div role="alert">
33
28
  * <p>{error.message}</p>
34
29
  * <button onClick={reset}>Try again</button>
35
30
  * </div>
36
31
  * )}>
37
32
  * <RiskyWidget />
38
- * </ErrorBoundary>
33
+ * </CatchBoundary>
39
34
  * ```
35
+ *
36
+ * @see {@link https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary | React — error boundaries}
37
+ * @see {@link https://www.rshono.com/docs/api#rshonocoreclient | Docs — `@rshono/core/client`}
40
38
  */
41
- export class ErrorBoundary extends Component {
39
+ export class CatchBoundary extends Component {
42
40
  state = { error: null };
43
41
  static getDerivedStateFromError(error) {
44
42
  return { error };
45
43
  }
46
44
  componentDidCatch(error) {
47
45
  if (isControlError(error))
48
- return; // a redirect isn't an error to report
46
+ return;
49
47
  this.props.onError?.(error);
50
48
  }
51
49
  componentDidUpdate(prev) {
@@ -61,41 +59,41 @@ export class ErrorBoundary extends Component {
61
59
  const { error } = this.state;
62
60
  if (error !== null) {
63
61
  if (isControlError(error))
64
- throw error; // navigation in flight — never show a fallback for it
62
+ throw error;
65
63
  const { fallback } = this.props;
66
64
  if (fallback === undefined)
67
- throw error; // no local fallback → propagate to an outer boundary
65
+ throw error; // propagate to an outer boundary
68
66
  return typeof fallback === 'function' ? fallback(error, this.reset) : fallback;
69
67
  }
70
68
  return this.props.children;
71
69
  }
72
70
  }
73
71
  /**
74
- * A loading + error boundary in one wrapper — the common case for an async
75
- * section of a page. It always renders the same shape:
72
+ * A loading and error boundary in one wrapper — the common case for an async section of a page. It
73
+ * always renders the same shape:
76
74
  *
77
75
  * ```tsx
78
- * <ErrorBoundary fallback={error}>
76
+ * <CatchBoundary fallback={error}>
79
77
  * <Suspense fallback={loading}>{children}</Suspense>
80
- * </ErrorBoundary>
78
+ * </CatchBoundary>
81
79
  * ```
82
80
  *
83
- * so `error` catches anything the children throw (including while suspended) and
84
- * `loading` shows until they resolve. Both fallbacks are optional: omit
85
- * `loading` and nothing shows while loading; omit `error` and thrown errors
86
- * propagate to the next boundary out (or the global error page) rather than
87
- * being caught here.
81
+ * so `loading` shows until the children resolve and `error` catches whatever they throw, suspended or
82
+ * not. `error` is optional: omit it and errors propagate to the next boundary out.
88
83
  *
89
84
  * @example
90
85
  * ```tsx
91
- * import { Boundary } from '@rshono/core/client';
86
+ * import { AsyncBoundary } from '@rshono/core/client';
92
87
  *
93
- * <Boundary loading={<Spinner />} error={(e, reset) => <Retry onClick={reset} />}>
88
+ * <AsyncBoundary loading={<Spinner />} error={(e, reset) => <Retry onClick={reset} />}>
94
89
  * <SlowServerComponent />
95
- * </Boundary>
90
+ * </AsyncBoundary>
96
91
  * ```
92
+ *
93
+ * @see {@link https://react.dev/reference/react/Suspense | React — `<Suspense>`}
94
+ * @see {@link https://www.rshono.com/docs/api#rshonocoreclient | Docs — `@rshono/core/client`}
97
95
  */
98
- export function Boundary({ loading = null, error, onError, resetKeys, children }) {
99
- return (_jsx(ErrorBoundary, { fallback: error, onError: onError, resetKeys: resetKeys, children: _jsx(Suspense, { fallback: loading, children: children }) }));
96
+ export function AsyncBoundary({ loading, error, onError, resetKeys, children }) {
97
+ return (_jsx(CatchBoundary, { fallback: error, onError: onError, resetKeys: resetKeys, children: _jsx(Suspense, { fallback: loading, children: children }) }));
100
98
  }
101
99
  //# sourceMappingURL=boundaries.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"boundaries.js","sourceRoot":"","sources":["../../src/runtime/boundaries.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;;GAKG;AACH,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,eAAe,CAAE,KAAqC,EAAE,MAAM,CAAC,CAAC;AACzE,CAAC;AAmCD,SAAS,WAAW,CAAC,CAAqB,EAAE,CAAqB;IAC/D,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,OAAO,aAAc,SAAQ,SAAiD;IAClF,KAAK,GAAuB,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAE5C,MAAM,CAAC,wBAAwB,CAAC,KAAY;QAC1C,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,iBAAiB,CAAC,KAAY;QAC5B,IAAI,cAAc,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,sCAAsC;QACzE,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,kBAAkB,CAAC,IAAwB;QACzC,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACjC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,IAAI,SAAS,IAAI,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC;YAC9F,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,GAAG,GAAS,EAAE;QACjB,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjC,CAAC,CAAC;IAEF,MAAM;QACJ,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC7B,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,IAAI,cAAc,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC,CAAC,sDAAsD;YAC9F,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;YAChC,IAAI,QAAQ,KAAK,SAAS;gBAAE,MAAM,KAAK,CAAC,CAAC,qDAAqD;YAC9F,OAAO,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QACjF,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC7B,CAAC;CACF;AAcD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAiB;IAC7F,OAAO,CACL,KAAC,aAAa,IAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,YACpE,KAAC,QAAQ,IAAC,QAAQ,EAAE,OAAO,YAAG,QAAQ,GAAY,GACpC,CACjB,CAAC;AACJ,CAAC","sourcesContent":["'use client';\n\nimport { Component, Suspense, type ReactNode } from 'react';\nimport { isControlDigest } from './control.js';\n\n/**\n * `redirect()` and `notFound()` reach the browser as a thrown error carrying a control digest.\n * They are navigation, not failure, so no boundary may absorb one — otherwise a `redirect()` from\n * a component inside a `<Boundary>` would render \"something went wrong\" instead of navigating.\n * They're re-thrown to the root, where the runtime turns the digest into a real navigation.\n */\nfunction isControlError(error: unknown): boolean {\n return isControlDigest((error as { digest?: unknown } | null)?.digest);\n}\n\n/**\n * What an {@link ErrorBoundary} / {@link Boundary} renders once a child throws.\n * Either a static node, or a render function that also gets a `reset` callback\n * to clear the error and re-render the children (e.g. a \"Try again\" button).\n *\n * The render-function form only works when the boundary is used from a `'use\n * client'` component — functions can't cross the server→client boundary. From a\n * server component, pass a `ReactNode`.\n */\nexport type ErrorFallback = ReactNode | ((error: Error, reset: () => void) => ReactNode);\n\nexport interface ErrorBoundaryProps {\n /**\n * Rendered in place of the children after one of them throws. Omit it to\n * report the error via `onError` and re-throw to the next boundary out (or\n * the global error page) instead of handling it here.\n */\n fallback?: ErrorFallback;\n /** Called with the caught error (for logging / reporting). */\n onError?: (error: Error) => void;\n /**\n * When any value in this array changes while the boundary is showing its\n * fallback, the error is cleared automatically. Pass the current pathname to\n * recover when the user navigates away: `resetKeys={[useNavigation().url.pathname]}`.\n */\n resetKeys?: readonly unknown[];\n children: ReactNode;\n}\n\ninterface ErrorBoundaryState {\n error: Error | null;\n}\n\nfunction keysChanged(a: readonly unknown[], b: readonly unknown[]): boolean {\n return a.length !== b.length || a.some((value, i) => !Object.is(value, b[i]));\n}\n\n/**\n * A general-purpose error boundary. Catches errors thrown while rendering its\n * children — a client island that blew up, or a server component that rejected\n * on a soft navigation — and renders `fallback` in their place instead of\n * tearing down the whole page.\n *\n * It's a `'use client'` component (React error boundaries must be), so drop it\n * anywhere in the tree from a server or client component. Use {@link Boundary}\n * when you also want a Suspense loading fallback in the same wrapper.\n *\n * @example\n * ```tsx\n * import { ErrorBoundary } from '@rshono/core/client';\n *\n * <ErrorBoundary fallback={(error, reset) => (\n * <div role=\"alert\">\n * <p>{error.message}</p>\n * <button onClick={reset}>Try again</button>\n * </div>\n * )}>\n * <RiskyWidget />\n * </ErrorBoundary>\n * ```\n */\nexport class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\n state: ErrorBoundaryState = { error: null };\n\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n return { error };\n }\n\n componentDidCatch(error: Error): void {\n if (isControlError(error)) return; // a redirect isn't an error to report\n this.props.onError?.(error);\n }\n\n componentDidUpdate(prev: ErrorBoundaryProps): void {\n const { resetKeys } = this.props;\n if (this.state.error && prev.resetKeys && resetKeys && keysChanged(prev.resetKeys, resetKeys)) {\n this.reset();\n }\n }\n\n reset = (): void => {\n this.setState({ error: null });\n };\n\n render(): ReactNode {\n const { error } = this.state;\n if (error !== null) {\n if (isControlError(error)) throw error; // navigation in flight — never show a fallback for it\n const { fallback } = this.props;\n if (fallback === undefined) throw error; // no local fallback → propagate to an outer boundary\n return typeof fallback === 'function' ? fallback(error, this.reset) : fallback;\n }\n return this.props.children;\n }\n}\n\nexport interface BoundaryProps {\n /** Suspense fallback, shown while the children (or their data) are still loading. */\n loading?: ReactNode;\n /** Error fallback, shown if a child throws. See {@link ErrorFallback}. */\n error?: ErrorFallback;\n /** Called with the caught error. */\n onError?: (error: Error) => void;\n /** Clears the error fallback when any value changes — see {@link ErrorBoundaryProps.resetKeys}. */\n resetKeys?: readonly unknown[];\n children: ReactNode;\n}\n\n/**\n * A loading + error boundary in one wrapper — the common case for an async\n * section of a page. It always renders the same shape:\n *\n * ```tsx\n * <ErrorBoundary fallback={error}>\n * <Suspense fallback={loading}>{children}</Suspense>\n * </ErrorBoundary>\n * ```\n *\n * so `error` catches anything the children throw (including while suspended) and\n * `loading` shows until they resolve. Both fallbacks are optional: omit\n * `loading` and nothing shows while loading; omit `error` and thrown errors\n * propagate to the next boundary out (or the global error page) rather than\n * being caught here.\n *\n * @example\n * ```tsx\n * import { Boundary } from '@rshono/core/client';\n *\n * <Boundary loading={<Spinner />} error={(e, reset) => <Retry onClick={reset} />}>\n * <SlowServerComponent />\n * </Boundary>\n * ```\n */\nexport function Boundary({ loading = null, error, onError, resetKeys, children }: BoundaryProps): ReactNode {\n return (\n <ErrorBoundary fallback={error} onError={onError} resetKeys={resetKeys}>\n <Suspense fallback={loading}>{children}</Suspense>\n </ErrorBoundary>\n );\n}\n"]}
1
+ {"version":3,"file":"boundaries.js","sourceRoot":"","sources":["../../src/runtime/boundaries.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,wGAAwG;AACxG,kGAAkG;AAClG,mDAAmD;AACnD,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,eAAe,CAAE,KAAqC,EAAE,MAAM,CAAC,CAAC;AACzE,CAAC;AA2CD,SAAS,WAAW,CAAC,CAAqB,EAAE,CAAqB;IAC/D,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,aAAc,SAAQ,SAAiD;IAClF,KAAK,GAAuB,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAE5C,MAAM,CAAC,wBAAwB,CAAC,KAAY;QAC1C,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,iBAAiB,CAAC,KAAY;QAC5B,IAAI,cAAc,CAAC,KAAK,CAAC;YAAE,OAAO;QAClC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,kBAAkB,CAAC,IAAwB;QACzC,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACjC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,IAAI,SAAS,IAAI,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC;YAC9F,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,GAAG,GAAS,EAAE;QACjB,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjC,CAAC,CAAC;IAEF,MAAM;QACJ,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC7B,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,IAAI,cAAc,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;YACvC,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;YAChC,IAAI,QAAQ,KAAK,SAAS;gBAAE,MAAM,KAAK,CAAC,CAAC,iCAAiC;YAC1E,OAAO,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QACjF,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC7B,CAAC;CACF;AAoBD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAsB;IAChG,OAAO,CACL,KAAC,aAAa,IAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,YACpE,KAAC,QAAQ,IAAC,QAAQ,EAAE,OAAO,YAAG,QAAQ,GAAY,GACpC,CACjB,CAAC;AACJ,CAAC","sourcesContent":["'use client';\n\nimport { Component, Suspense, type ReactNode } from 'react';\nimport { isControlDigest } from './control.js';\n\n// `redirect()` and `notFound()` reach the browser as a thrown error carrying a control digest. They are\n// navigation, not failure, so no boundary absorbs one — they are re-thrown to the root, where the\n// runtime turns the digest into a real navigation.\nfunction isControlError(error: unknown): boolean {\n return isControlDigest((error as { digest?: unknown } | null)?.digest);\n}\n\n/**\n * What a {@link CatchBoundary} / {@link AsyncBoundary} renders once a child throws: either a static\n * node, or a render function given the error and a `reset` callback that clears it and re-renders the\n * children (a \"Try again\" button, say).\n *\n * The function form only works from a `'use client'` component — functions can't cross the\n * server→client boundary. From a server component, pass a `ReactNode`.\n */\nexport type ErrorFallback = ReactNode | ((error: Error, reset: () => void) => ReactNode);\n\n/** Props for {@link CatchBoundary}. */\nexport interface CatchBoundaryProps {\n /**\n * Rendered in place of the children after one of them throws. Omit it to report the error via\n * `onError` and re-throw to the next boundary out or the app's `error` page instead of handling it\n * here.\n */\n fallback?: ErrorFallback;\n /**\n * Called with the caught error, for logging or reporting.\n *\n * A function prop, so — like {@link ErrorFallback}'s function form — it can only be passed from a\n * `'use client'` component. React refuses one from a server component by name: \"Event handlers cannot be\n * passed to Client Component props\".\n */\n onError?: (error: Error) => void;\n /**\n * Clears the error automatically when any value in this array changes while the fallback is showing.\n * Pass the current pathname to recover when the user navigates away — `resetKeys={[url.pathname]}` from a\n * page's `url` prop, which is the form that works from the server component rendering this boundary, or\n * `resetKeys={[useNavigation().url.pathname]}` inside a `'use client'` component.\n */\n resetKeys?: readonly unknown[];\n /** The subtree this boundary protects. */\n children: ReactNode;\n}\n\ninterface CatchBoundaryState {\n error: Error | null;\n}\n\nfunction keysChanged(a: readonly unknown[], b: readonly unknown[]): boolean {\n return a.length !== b.length || a.some((value, i) => !Object.is(value, b[i]));\n}\n\n/**\n * A general-purpose error boundary: catches what its children throw — a client island that blew up, a\n * server component that rejected on a soft navigation — and renders `fallback` in their place rather\n * than tearing down the page.\n *\n * It is a `'use client'` component (React error boundaries must be), so a server component can render\n * it too. Reach for {@link AsyncBoundary} when you also want a Suspense loading fallback.\n *\n * @example\n * ```tsx\n * import { CatchBoundary } from '@rshono/core/client';\n *\n * <CatchBoundary fallback={(error, reset) => (\n * <div role=\"alert\">\n * <p>{error.message}</p>\n * <button onClick={reset}>Try again</button>\n * </div>\n * )}>\n * <RiskyWidget />\n * </CatchBoundary>\n * ```\n *\n * @see {@link https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary | React — error boundaries}\n * @see {@link https://www.rshono.com/docs/api#rshonocoreclient | Docs — `@rshono/core/client`}\n */\nexport class CatchBoundary extends Component<CatchBoundaryProps, CatchBoundaryState> {\n state: CatchBoundaryState = { error: null };\n\n static getDerivedStateFromError(error: Error): CatchBoundaryState {\n return { error };\n }\n\n componentDidCatch(error: Error): void {\n if (isControlError(error)) return;\n this.props.onError?.(error);\n }\n\n componentDidUpdate(prev: CatchBoundaryProps): void {\n const { resetKeys } = this.props;\n if (this.state.error && prev.resetKeys && resetKeys && keysChanged(prev.resetKeys, resetKeys)) {\n this.reset();\n }\n }\n\n reset = (): void => {\n this.setState({ error: null });\n };\n\n render(): ReactNode {\n const { error } = this.state;\n if (error !== null) {\n if (isControlError(error)) throw error;\n const { fallback } = this.props;\n if (fallback === undefined) throw error; // propagate to an outer boundary\n return typeof fallback === 'function' ? fallback(error, this.reset) : fallback;\n }\n return this.props.children;\n }\n}\n\n/** Props for {@link AsyncBoundary}. */\nexport interface AsyncBoundaryProps {\n /**\n * Suspense fallback, shown while the children or their data are still loading. Required — a loading\n * state is the reason to reach for this over {@link CatchBoundary}, so showing nothing is an explicit\n * `loading={null}`.\n */\n loading: ReactNode;\n /** Error fallback, shown if a child throws. See {@link ErrorFallback}. */\n error?: ErrorFallback;\n /** Called with the caught error. From a `'use client'` component only — see {@link CatchBoundaryProps.onError}. */\n onError?: (error: Error) => void;\n /** Clears the error fallback when any value changes — see {@link CatchBoundaryProps.resetKeys}. */\n resetKeys?: readonly unknown[];\n /** The subtree this boundary suspends on and protects. */\n children: ReactNode;\n}\n\n/**\n * A loading and error boundary in one wrapper — the common case for an async section of a page. It\n * always renders the same shape:\n *\n * ```tsx\n * <CatchBoundary fallback={error}>\n * <Suspense fallback={loading}>{children}</Suspense>\n * </CatchBoundary>\n * ```\n *\n * so `loading` shows until the children resolve and `error` catches whatever they throw, suspended or\n * not. `error` is optional: omit it and errors propagate to the next boundary out.\n *\n * @example\n * ```tsx\n * import { AsyncBoundary } from '@rshono/core/client';\n *\n * <AsyncBoundary loading={<Spinner />} error={(e, reset) => <Retry onClick={reset} />}>\n * <SlowServerComponent />\n * </AsyncBoundary>\n * ```\n *\n * @see {@link https://react.dev/reference/react/Suspense | React — `<Suspense>`}\n * @see {@link https://www.rshono.com/docs/api#rshonocoreclient | Docs — `@rshono/core/client`}\n */\nexport function AsyncBoundary({ loading, error, onError, resetKeys, children }: AsyncBoundaryProps): ReactNode {\n return (\n <CatchBoundary fallback={error} onError={onError} resetKeys={resetKeys}>\n <Suspense fallback={loading}>{children}</Suspense>\n </CatchBoundary>\n );\n}\n"]}
@@ -1,16 +1,25 @@
1
1
  /**
2
- * `@rshono/core/client` — the browser-side surface, for use from `'use client'`
3
- * modules: {@link useNavigation} for the current URL and soft navigation, and
4
- * {@link Boundary} / {@link ErrorBoundary} / {@link NavigationProgress} as
5
- * components.
2
+ * `@rshono/core/client` — the browser-side surface: `useNavigation()` for the current URL and soft
3
+ * navigation, plus the `<AsyncBoundary>` and `<CatchBoundary>` components.
6
4
  *
7
- * Every export is itself a `'use client'` module, so a server component can
8
- * render {@link Boundary} or {@link NavigationProgress} directly but the hook
9
- * needs a client component. In a server component, read the same request data
10
- * from `getContext()` in `@rshono/core/server`.
5
+ * Every export is itself a `'use client'` module, so a server component can render `<AsyncBoundary>`
6
+ * directly but the hook needs a client component. In a server component, read the same request data
7
+ * from `getRequestContext()` in `@rshono/core/server`.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * 'use client';
12
+ * import { useNavigation } from '@rshono/core/client';
13
+ *
14
+ * export function Tab() {
15
+ * const { url, router } = useNavigation();
16
+ * return <button onClick={() => router.push(`${url.pathname}?tab=details`)}>Details</button>;
17
+ * }
18
+ * ```
19
+ *
20
+ * @see {@link https://www.rshono.com/docs/api#rshonocoreclient | Docs — `@rshono/core/client`}
11
21
  *
12
22
  * @packageDocumentation
13
23
  */
14
- export { NavigationProgress, useNavigation, type Navigation, type NavigationProgressProps, type Router } from './navigation.js';
15
- export { Boundary, ErrorBoundary, type BoundaryProps, type ErrorBoundaryProps, type ErrorFallback } from './boundaries.js';
16
- //# sourceMappingURL=client.d.ts.map
24
+ export { useNavigation, type NavigationRouter, type NavigationState } from './navigation.js';
25
+ export { AsyncBoundary, CatchBoundary, type AsyncBoundaryProps, type CatchBoundaryProps, type ErrorFallback } from './boundaries.js';
@@ -1,16 +1,26 @@
1
1
  /**
2
- * `@rshono/core/client` — the browser-side surface, for use from `'use client'`
3
- * modules: {@link useNavigation} for the current URL and soft navigation, and
4
- * {@link Boundary} / {@link ErrorBoundary} / {@link NavigationProgress} as
5
- * components.
2
+ * `@rshono/core/client` — the browser-side surface: `useNavigation()` for the current URL and soft
3
+ * navigation, plus the `<AsyncBoundary>` and `<CatchBoundary>` components.
6
4
  *
7
- * Every export is itself a `'use client'` module, so a server component can
8
- * render {@link Boundary} or {@link NavigationProgress} directly but the hook
9
- * needs a client component. In a server component, read the same request data
10
- * from `getContext()` in `@rshono/core/server`.
5
+ * Every export is itself a `'use client'` module, so a server component can render `<AsyncBoundary>`
6
+ * directly but the hook needs a client component. In a server component, read the same request data
7
+ * from `getRequestContext()` in `@rshono/core/server`.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * 'use client';
12
+ * import { useNavigation } from '@rshono/core/client';
13
+ *
14
+ * export function Tab() {
15
+ * const { url, router } = useNavigation();
16
+ * return <button onClick={() => router.push(`${url.pathname}?tab=details`)}>Details</button>;
17
+ * }
18
+ * ```
19
+ *
20
+ * @see {@link https://www.rshono.com/docs/api#rshonocoreclient | Docs — `@rshono/core/client`}
11
21
  *
12
22
  * @packageDocumentation
13
23
  */
14
- export { NavigationProgress, useNavigation } from './navigation.js';
15
- export { Boundary, ErrorBoundary } from './boundaries.js';
24
+ export { useNavigation } from './navigation.js';
25
+ export { AsyncBoundary, CatchBoundary } from './boundaries.js';
16
26
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/runtime/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAA8D,MAAM,iBAAiB,CAAC;AAChI,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAmE,MAAM,iBAAiB,CAAC","sourcesContent":["/**\n * `@rshono/core/client` — the browser-side surface, for use from `'use client'`\n * modules: {@link useNavigation} for the current URL and soft navigation, and\n * {@link Boundary} / {@link ErrorBoundary} / {@link NavigationProgress} as\n * components.\n *\n * Every export is itself a `'use client'` module, so a server component can\n * render {@link Boundary} or {@link NavigationProgress} directly — but the hook\n * needs a client component. In a server component, read the same request data\n * from `getContext()` in `@rshono/core/server`.\n *\n * @packageDocumentation\n */\n\nexport { NavigationProgress, useNavigation, type Navigation, type NavigationProgressProps, type Router } from './navigation.js';\nexport { Boundary, ErrorBoundary, type BoundaryProps, type ErrorBoundaryProps, type ErrorFallback } from './boundaries.js';\n"]}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/runtime/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,aAAa,EAA+C,MAAM,iBAAiB,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,aAAa,EAAwE,MAAM,iBAAiB,CAAC","sourcesContent":["/**\n * `@rshono/core/client` — the browser-side surface: `useNavigation()` for the current URL and soft\n * navigation, plus the `<AsyncBoundary>` and `<CatchBoundary>` components.\n *\n * Every export is itself a `'use client'` module, so a server component can render `<AsyncBoundary>`\n * directly — but the hook needs a client component. In a server component, read the same request data\n * from `getRequestContext()` in `@rshono/core/server`.\n *\n * @example\n * ```tsx\n * 'use client';\n * import { useNavigation } from '@rshono/core/client';\n *\n * export function Tab() {\n * const { url, router } = useNavigation();\n * return <button onClick={() => router.push(`${url.pathname}?tab=details`)}>Details</button>;\n * }\n * ```\n *\n * @see {@link https://www.rshono.com/docs/api#rshonocoreclient | Docs — `@rshono/core/client`}\n *\n * @packageDocumentation\n */\n\nexport { useNavigation, type NavigationRouter, type NavigationState } from './navigation.js';\nexport { AsyncBoundary, CatchBoundary, type AsyncBoundaryProps, type CatchBoundaryProps, type ErrorFallback } from './boundaries.js';\n"]}