@rshono/core 1.0.0-rc.4 → 1.0.0-rc.5
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/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/boundaries.d.ts +29 -25
- package/dist/runtime/boundaries.d.ts.map +1 -1
- package/dist/runtime/boundaries.js +16 -17
- package/dist/runtime/boundaries.js.map +1 -1
- package/dist/runtime/client.d.ts +3 -3
- package/dist/runtime/client.d.ts.map +1 -1
- package/dist/runtime/client.js +3 -3
- package/dist/runtime/client.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* server components and actions (`getRequestContext`, `redirect`, `notFound`), plus
|
|
9
9
|
* `onServerError` for reporting the errors the framework catches.
|
|
10
10
|
* - `@rshono/core/client` — hooks and components for `'use client'` modules
|
|
11
|
-
* (`useNavigation`, `
|
|
11
|
+
* (`useNavigation`, `AsyncBoundary`, `CatchBoundary`).
|
|
12
12
|
*
|
|
13
13
|
* @packageDocumentation
|
|
14
14
|
*/
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* server components and actions (`getRequestContext`, `redirect`, `notFound`), plus
|
|
9
9
|
* `onServerError` for reporting the errors the framework catches.
|
|
10
10
|
* - `@rshono/core/client` — hooks and components for `'use client'` modules
|
|
11
|
-
* (`useNavigation`, `
|
|
11
|
+
* (`useNavigation`, `AsyncBoundary`, `CatchBoundary`).
|
|
12
12
|
*
|
|
13
13
|
* @packageDocumentation
|
|
14
14
|
*/
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,YAAY,GAab,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,YAAY,EAA6C,MAAM,aAAa,CAAC;AAItF,kGAAkG;AAClG,sGAAsG;AACtG,mGAAmG","sourcesContent":["/**\n * `@rshono/core` — the build-time surface: route and config declaration plus the types\n * your pages and endpoints are written against. Everything here is safe to\n * import from server code; none of it pulls in runtime machinery.\n *\n * The two companion entry points are runtime-only:\n * - `@rshono/core/server` — {@link https://hono.dev | Hono} request context inside\n * server components and actions (`getRequestContext`, `redirect`, `notFound`), plus\n * `onServerError` for reporting the errors the framework catches.\n * - `@rshono/core/client` — hooks and components for `'use client'` modules\n * (`useNavigation`, `
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,YAAY,GAab,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,YAAY,EAA6C,MAAM,aAAa,CAAC;AAItF,kGAAkG;AAClG,sGAAsG;AACtG,mGAAmG","sourcesContent":["/**\n * `@rshono/core` — the build-time surface: route and config declaration plus the types\n * your pages and endpoints are written against. Everything here is safe to\n * import from server code; none of it pulls in runtime machinery.\n *\n * The two companion entry points are runtime-only:\n * - `@rshono/core/server` — {@link https://hono.dev | Hono} request context inside\n * server components and actions (`getRequestContext`, `redirect`, `notFound`), plus\n * `onServerError` for reporting the errors the framework catches.\n * - `@rshono/core/client` — hooks and components for `'use client'` modules\n * (`useNavigation`, `AsyncBoundary`, `CatchBoundary`).\n *\n * @packageDocumentation\n */\n\nexport {\n defineRoutes,\n type EndpointRoute,\n type EndpointServerModule,\n type ErrorPageInfo,\n type ErrorPageProps,\n type FallbackPage,\n type HTTPMethod,\n type PageComponent,\n type PageProps,\n type PageRoute,\n type PathParams,\n type Route,\n type RouteConfig,\n} from './router.js';\n\nexport { defineConfig, type RshonoConfig, type RspackHookContext } from './config.js';\n\nexport type { DeployTarget } from './deploy/contract.js';\n\n// Hono's own `Context` and `Handler` used to be re-exported from here \"for convenience\". They are\n// Hono's types, `hono` is a peer dependency every app already has, and importing them from two places\n// only raised the question of which one is right — so an endpoint module imports them from `hono`.\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Component, type ReactNode } from 'react';
|
|
2
2
|
/**
|
|
3
|
-
* What
|
|
3
|
+
* What a {@link CatchBoundary} / {@link AsyncBoundary} renders once a child throws.
|
|
4
4
|
* Either a static node, or a render function that also gets a `reset` callback
|
|
5
5
|
* to clear the error and re-render the children (e.g. a "Try again" button).
|
|
6
6
|
*
|
|
@@ -9,7 +9,7 @@ import { Component, type ReactNode } from 'react';
|
|
|
9
9
|
* server component, pass a `ReactNode`.
|
|
10
10
|
*/
|
|
11
11
|
export type ErrorFallback = ReactNode | ((error: Error, reset: () => void) => ReactNode);
|
|
12
|
-
export interface
|
|
12
|
+
export interface CatchBoundaryProps {
|
|
13
13
|
/**
|
|
14
14
|
* Rendered in place of the children after one of them throws. Omit it to
|
|
15
15
|
* report the error via `onError` and re-throw to the next boundary out (or
|
|
@@ -26,7 +26,7 @@ export interface ErrorBoundaryProps {
|
|
|
26
26
|
resetKeys?: readonly unknown[];
|
|
27
27
|
children: ReactNode;
|
|
28
28
|
}
|
|
29
|
-
interface
|
|
29
|
+
interface CatchBoundaryState {
|
|
30
30
|
error: Error | null;
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
@@ -36,39 +36,44 @@ interface ErrorBoundaryState {
|
|
|
36
36
|
* tearing down the whole page.
|
|
37
37
|
*
|
|
38
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
|
|
39
|
+
* anywhere in the tree from a server or client component. Use {@link AsyncBoundary}
|
|
40
40
|
* when you also want a Suspense loading fallback in the same wrapper.
|
|
41
41
|
*
|
|
42
42
|
* @example
|
|
43
43
|
* ```tsx
|
|
44
|
-
* import {
|
|
44
|
+
* import { CatchBoundary } from '@rshono/core/client';
|
|
45
45
|
*
|
|
46
|
-
* <
|
|
46
|
+
* <CatchBoundary fallback={(error, reset) => (
|
|
47
47
|
* <div role="alert">
|
|
48
48
|
* <p>{error.message}</p>
|
|
49
49
|
* <button onClick={reset}>Try again</button>
|
|
50
50
|
* </div>
|
|
51
51
|
* )}>
|
|
52
52
|
* <RiskyWidget />
|
|
53
|
-
* </
|
|
53
|
+
* </CatchBoundary>
|
|
54
54
|
* ```
|
|
55
55
|
*/
|
|
56
|
-
export declare class
|
|
57
|
-
state:
|
|
58
|
-
static getDerivedStateFromError(error: Error):
|
|
56
|
+
export declare class CatchBoundary extends Component<CatchBoundaryProps, CatchBoundaryState> {
|
|
57
|
+
state: CatchBoundaryState;
|
|
58
|
+
static getDerivedStateFromError(error: Error): CatchBoundaryState;
|
|
59
59
|
componentDidCatch(error: Error): void;
|
|
60
|
-
componentDidUpdate(prev:
|
|
60
|
+
componentDidUpdate(prev: CatchBoundaryProps): void;
|
|
61
61
|
reset: () => void;
|
|
62
62
|
render(): ReactNode;
|
|
63
63
|
}
|
|
64
|
-
export interface
|
|
65
|
-
/**
|
|
66
|
-
|
|
64
|
+
export interface AsyncBoundaryProps {
|
|
65
|
+
/**
|
|
66
|
+
* Suspense fallback, shown while the children (or their data) are still
|
|
67
|
+
* loading. Required — a loading state is the reason to reach for this over
|
|
68
|
+
* {@link CatchBoundary}, so showing nothing is an explicit `loading={null}`
|
|
69
|
+
* rather than something you get by leaving the prop off.
|
|
70
|
+
*/
|
|
71
|
+
loading: ReactNode;
|
|
67
72
|
/** Error fallback, shown if a child throws. See {@link ErrorFallback}. */
|
|
68
73
|
error?: ErrorFallback;
|
|
69
74
|
/** Called with the caught error. */
|
|
70
75
|
onError?: (error: Error) => void;
|
|
71
|
-
/** Clears the error fallback when any value changes — see {@link
|
|
76
|
+
/** Clears the error fallback when any value changes — see {@link CatchBoundaryProps.resetKeys}. */
|
|
72
77
|
resetKeys?: readonly unknown[];
|
|
73
78
|
children: ReactNode;
|
|
74
79
|
}
|
|
@@ -77,26 +82,25 @@ export interface BoundaryProps {
|
|
|
77
82
|
* section of a page. It always renders the same shape:
|
|
78
83
|
*
|
|
79
84
|
* ```tsx
|
|
80
|
-
* <
|
|
85
|
+
* <CatchBoundary fallback={error}>
|
|
81
86
|
* <Suspense fallback={loading}>{children}</Suspense>
|
|
82
|
-
* </
|
|
87
|
+
* </CatchBoundary>
|
|
83
88
|
* ```
|
|
84
89
|
*
|
|
85
90
|
* so `error` catches anything the children throw (including while suspended) and
|
|
86
|
-
* `loading` shows until they resolve.
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* being caught here.
|
|
91
|
+
* `loading` shows until they resolve. `error` is optional: omit it and thrown
|
|
92
|
+
* errors propagate to the next boundary out (or the global error page) rather
|
|
93
|
+
* than being caught here.
|
|
90
94
|
*
|
|
91
95
|
* @example
|
|
92
96
|
* ```tsx
|
|
93
|
-
* import {
|
|
97
|
+
* import { AsyncBoundary } from '@rshono/core/client';
|
|
94
98
|
*
|
|
95
|
-
* <
|
|
99
|
+
* <AsyncBoundary loading={<Spinner />} error={(e, reset) => <Retry onClick={reset} />}>
|
|
96
100
|
* <SlowServerComponent />
|
|
97
|
-
* </
|
|
101
|
+
* </AsyncBoundary>
|
|
98
102
|
* ```
|
|
99
103
|
*/
|
|
100
|
-
export declare function
|
|
104
|
+
export declare function AsyncBoundary({ loading, error, onError, resetKeys, children }: AsyncBoundaryProps): ReactNode;
|
|
101
105
|
export {};
|
|
102
106
|
//# sourceMappingURL=boundaries.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boundaries.d.ts","sourceRoot":"","sources":["../../src/runtime/boundaries.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAa5D;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,IAAI,KAAK,SAAS,CAAC,CAAC;AAEzF,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC/B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,UAAU,kBAAkB;IAC1B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAMD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,aAAc,SAAQ,SAAS,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;IAClF,KAAK,EAAE,kBAAkB,CAAmB;IAE5C,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,kBAAkB,CAEhE;IAED,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAGpC;IAED,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAKjD;IAED,KAAK,QAAO,IAAI,CAEd;IAEF,MAAM,IAAI,SAAS,CASlB;CACF;AAED,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"boundaries.d.ts","sourceRoot":"","sources":["../../src/runtime/boundaries.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAa5D;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,IAAI,KAAK,SAAS,CAAC,CAAC;AAEzF,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC/B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,UAAU,kBAAkB;IAC1B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAMD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,aAAc,SAAQ,SAAS,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;IAClF,KAAK,EAAE,kBAAkB,CAAmB;IAE5C,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,kBAAkB,CAEhE;IAED,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAGpC;IAED,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAKjD;IAED,KAAK,QAAO,IAAI,CAEd;IAEF,MAAM,IAAI,SAAS,CASlB;CACF;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,OAAO,EAAE,SAAS,CAAC;IACnB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,oCAAoC;IACpC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,mGAAmG;IACnG,SAAS,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC/B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,kBAAkB,GAAG,SAAS,CAM7G"}
|
|
@@ -5,7 +5,7 @@ import { isControlDigest } from './control.js';
|
|
|
5
5
|
/**
|
|
6
6
|
* `redirect()` and `notFound()` reach the browser as a thrown error carrying a control digest.
|
|
7
7
|
* They are navigation, not failure, so no boundary may absorb one — otherwise a `redirect()` from
|
|
8
|
-
* a component inside a `<
|
|
8
|
+
* a component inside a `<CatchBoundary>` would render "something went wrong" instead of navigating.
|
|
9
9
|
* They're re-thrown to the root, where the runtime turns the digest into a real navigation.
|
|
10
10
|
*/
|
|
11
11
|
function isControlError(error) {
|
|
@@ -21,24 +21,24 @@ function keysChanged(a, b) {
|
|
|
21
21
|
* tearing down the whole page.
|
|
22
22
|
*
|
|
23
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
|
|
24
|
+
* anywhere in the tree from a server or client component. Use {@link AsyncBoundary}
|
|
25
25
|
* when you also want a Suspense loading fallback in the same wrapper.
|
|
26
26
|
*
|
|
27
27
|
* @example
|
|
28
28
|
* ```tsx
|
|
29
|
-
* import {
|
|
29
|
+
* import { CatchBoundary } from '@rshono/core/client';
|
|
30
30
|
*
|
|
31
|
-
* <
|
|
31
|
+
* <CatchBoundary fallback={(error, reset) => (
|
|
32
32
|
* <div role="alert">
|
|
33
33
|
* <p>{error.message}</p>
|
|
34
34
|
* <button onClick={reset}>Try again</button>
|
|
35
35
|
* </div>
|
|
36
36
|
* )}>
|
|
37
37
|
* <RiskyWidget />
|
|
38
|
-
* </
|
|
38
|
+
* </CatchBoundary>
|
|
39
39
|
* ```
|
|
40
40
|
*/
|
|
41
|
-
export class
|
|
41
|
+
export class CatchBoundary extends Component {
|
|
42
42
|
state = { error: null };
|
|
43
43
|
static getDerivedStateFromError(error) {
|
|
44
44
|
return { error };
|
|
@@ -75,27 +75,26 @@ export class ErrorBoundary extends Component {
|
|
|
75
75
|
* section of a page. It always renders the same shape:
|
|
76
76
|
*
|
|
77
77
|
* ```tsx
|
|
78
|
-
* <
|
|
78
|
+
* <CatchBoundary fallback={error}>
|
|
79
79
|
* <Suspense fallback={loading}>{children}</Suspense>
|
|
80
|
-
* </
|
|
80
|
+
* </CatchBoundary>
|
|
81
81
|
* ```
|
|
82
82
|
*
|
|
83
83
|
* so `error` catches anything the children throw (including while suspended) and
|
|
84
|
-
* `loading` shows until they resolve.
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* being caught here.
|
|
84
|
+
* `loading` shows until they resolve. `error` is optional: omit it and thrown
|
|
85
|
+
* errors propagate to the next boundary out (or the global error page) rather
|
|
86
|
+
* than being caught here.
|
|
88
87
|
*
|
|
89
88
|
* @example
|
|
90
89
|
* ```tsx
|
|
91
|
-
* import {
|
|
90
|
+
* import { AsyncBoundary } from '@rshono/core/client';
|
|
92
91
|
*
|
|
93
|
-
* <
|
|
92
|
+
* <AsyncBoundary loading={<Spinner />} error={(e, reset) => <Retry onClick={reset} />}>
|
|
94
93
|
* <SlowServerComponent />
|
|
95
|
-
* </
|
|
94
|
+
* </AsyncBoundary>
|
|
96
95
|
* ```
|
|
97
96
|
*/
|
|
98
|
-
export function
|
|
99
|
-
return (_jsx(
|
|
97
|
+
export function AsyncBoundary({ loading, error, onError, resetKeys, children }) {
|
|
98
|
+
return (_jsx(CatchBoundary, { fallback: error, onError: onError, resetKeys: resetKeys, children: _jsx(Suspense, { fallback: loading, children: children }) }));
|
|
100
99
|
}
|
|
101
100
|
//# 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;
|
|
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;AAmBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;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/**\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 `<CatchBoundary>` 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 a {@link CatchBoundary} / {@link AsyncBoundary} 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 CatchBoundaryProps {\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 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 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 AsyncBoundary}\n * when you also want a Suspense loading fallback in the same wrapper.\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 */\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; // a redirect isn't an error to report\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; // 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 AsyncBoundaryProps {\n /**\n * Suspense fallback, shown while the children (or their data) are still\n * loading. Required — a loading state is the reason to reach for this over\n * {@link CatchBoundary}, so showing nothing is an explicit `loading={null}`\n * rather than something you get by leaving the prop off.\n */\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 CatchBoundaryProps.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 * <CatchBoundary fallback={error}>\n * <Suspense fallback={loading}>{children}</Suspense>\n * </CatchBoundary>\n * ```\n *\n * so `error` catches anything the children throw (including while suspended) and\n * `loading` shows until they resolve. `error` is optional: omit it and thrown\n * errors propagate to the next boundary out (or the global error page) rather\n * than being caught here.\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 */\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"]}
|
package/dist/runtime/client.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@rshono/core/client` — the browser-side surface, for use from `'use client'`
|
|
3
3
|
* modules: {@link useNavigation} for the current URL and soft navigation, and
|
|
4
|
-
* {@link
|
|
4
|
+
* {@link AsyncBoundary} / {@link CatchBoundary} as components.
|
|
5
5
|
*
|
|
6
6
|
* Every export is itself a `'use client'` module, so a server component can
|
|
7
|
-
* render {@link
|
|
7
|
+
* render {@link AsyncBoundary} directly — but the hook needs a client component. In a
|
|
8
8
|
* server component, read the same request data from `getRequestContext()` in
|
|
9
9
|
* `@rshono/core/server`.
|
|
10
10
|
*
|
|
11
11
|
* @packageDocumentation
|
|
12
12
|
*/
|
|
13
13
|
export { useNavigation, type NavigationRouter, type NavigationState } from './navigation.js';
|
|
14
|
-
export {
|
|
14
|
+
export { AsyncBoundary, CatchBoundary, type AsyncBoundaryProps, type CatchBoundaryProps, type ErrorFallback } from './boundaries.js';
|
|
15
15
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/runtime/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,aAAa,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC7F,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/runtime/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,aAAa,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/runtime/client.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@rshono/core/client` — the browser-side surface, for use from `'use client'`
|
|
3
3
|
* modules: {@link useNavigation} for the current URL and soft navigation, and
|
|
4
|
-
* {@link
|
|
4
|
+
* {@link AsyncBoundary} / {@link CatchBoundary} as components.
|
|
5
5
|
*
|
|
6
6
|
* Every export is itself a `'use client'` module, so a server component can
|
|
7
|
-
* render {@link
|
|
7
|
+
* render {@link AsyncBoundary} directly — but the hook needs a client component. In a
|
|
8
8
|
* server component, read the same request data from `getRequestContext()` in
|
|
9
9
|
* `@rshono/core/server`.
|
|
10
10
|
*
|
|
11
11
|
* @packageDocumentation
|
|
12
12
|
*/
|
|
13
13
|
export { useNavigation } from './navigation.js';
|
|
14
|
-
export {
|
|
14
|
+
export { AsyncBoundary, CatchBoundary } from './boundaries.js';
|
|
15
15
|
//# sourceMappingURL=client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/runtime/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,aAAa,EAA+C,MAAM,iBAAiB,CAAC;AAC7F,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/runtime/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;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, for use from `'use client'`\n * modules: {@link useNavigation} for the current URL and soft navigation, and\n * {@link AsyncBoundary} / {@link CatchBoundary} as components.\n *\n * Every export is itself a `'use client'` module, so a server component can\n * render {@link AsyncBoundary} directly — but the hook needs a client component. In a\n * server component, read the same request data from `getRequestContext()` in\n * `@rshono/core/server`.\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"]}
|
package/package.json
CHANGED