@rshono/core 1.0.0-rc.3 → 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/README.md +42 -26
- package/dist/builder/rspack-config.d.ts +3 -3
- package/dist/builder/rspack-config.js +3 -3
- package/dist/builder/rspack-config.js.map +1 -1
- package/dist/cli/build.d.ts +2 -2
- package/dist/cli/build.js.map +1 -1
- package/dist/cli/dev.d.ts +2 -2
- package/dist/cli/dev.js.map +1 -1
- package/dist/cli/index.js +11 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/config.d.ts +4 -17
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/deploy/cloudflare/build.js +1 -1
- package/dist/deploy/cloudflare/build.js.map +1 -1
- package/dist/deploy/cloudflare/runtime.d.ts.map +1 -1
- package/dist/deploy/cloudflare/runtime.js +7 -37
- package/dist/deploy/cloudflare/runtime.js.map +1 -1
- package/dist/deploy/contract.d.ts +14 -13
- package/dist/deploy/contract.d.ts.map +1 -1
- package/dist/deploy/contract.js.map +1 -1
- package/dist/deploy/filesystem.d.ts +1 -1
- package/dist/deploy/filesystem.d.ts.map +1 -1
- package/dist/deploy/filesystem.js +1 -3
- package/dist/deploy/filesystem.js.map +1 -1
- package/dist/deploy/node/runtime.d.ts +6 -2
- package/dist/deploy/node/runtime.d.ts.map +1 -1
- package/dist/deploy/node/runtime.js +30 -5
- package/dist/deploy/node/runtime.js.map +1 -1
- package/dist/deploy/presets.d.ts +5 -1
- package/dist/deploy/presets.d.ts.map +1 -1
- package/dist/deploy/presets.js +15 -28
- package/dist/deploy/presets.js.map +1 -1
- package/dist/deploy/vercel/runtime.d.ts.map +1 -1
- package/dist/deploy/vercel/runtime.js +0 -3
- package/dist/deploy/vercel/runtime.js.map +1 -1
- package/dist/index.d.ts +4 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/router.d.ts +14 -15
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +5 -6
- package/dist/router.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 +6 -7
- package/dist/runtime/client.d.ts.map +1 -1
- package/dist/runtime/client.js +6 -7
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/context.d.ts +36 -40
- package/dist/runtime/context.d.ts.map +1 -1
- package/dist/runtime/context.js +41 -53
- package/dist/runtime/context.js.map +1 -1
- package/dist/runtime/entry.client.d.ts +4 -0
- package/dist/runtime/entry.client.d.ts.map +1 -1
- package/dist/runtime/entry.client.js +161 -147
- package/dist/runtime/entry.client.js.map +1 -1
- package/dist/runtime/entry.rsc.d.ts.map +1 -1
- package/dist/runtime/entry.rsc.js +80 -63
- package/dist/runtime/entry.rsc.js.map +1 -1
- package/dist/runtime/entry.ssr.d.ts +9 -1
- package/dist/runtime/entry.ssr.d.ts.map +1 -1
- package/dist/runtime/entry.ssr.js +36 -18
- package/dist/runtime/entry.ssr.js.map +1 -1
- package/dist/runtime/flight-inject.d.ts +31 -0
- package/dist/runtime/flight-inject.d.ts.map +1 -0
- package/dist/runtime/flight-inject.js +221 -0
- package/dist/runtime/flight-inject.js.map +1 -0
- package/dist/runtime/navigation.d.ts +10 -39
- package/dist/runtime/navigation.d.ts.map +1 -1
- package/dist/runtime/navigation.js +7 -53
- package/dist/runtime/navigation.js.map +1 -1
- package/dist/runtime/request.d.ts +6 -0
- package/dist/runtime/request.d.ts.map +1 -1
- package/dist/runtime/request.js +8 -0
- package/dist/runtime/request.js.map +1 -1
- package/dist/runtime/server.d.ts +2 -2
- package/dist/runtime/server.d.ts.map +1 -1
- package/dist/runtime/server.js +2 -2
- package/dist/runtime/server.js.map +1 -1
- package/dist/server/headers.d.ts +9 -9
- package/dist/server/headers.js +9 -9
- package/dist/server/headers.js.map +1 -1
- package/dist/server/load-config.d.ts +2 -2
- package/dist/server/load-config.d.ts.map +1 -1
- package/dist/server/load-config.js +22 -11
- package/dist/server/load-config.js.map +1 -1
- package/dist/server/prerendered.d.ts +52 -9
- package/dist/server/prerendered.d.ts.map +1 -1
- package/dist/server/prerendered.js +57 -0
- package/dist/server/prerendered.js.map +1 -1
- package/dist/server/server-config.d.ts +11 -18
- package/dist/server/server-config.d.ts.map +1 -1
- package/dist/server/server-config.js +9 -12
- package/dist/server/server-config.js.map +1 -1
- package/dist/server/ssg.d.ts +2 -2
- package/dist/server/ssg.d.ts.map +1 -1
- package/dist/server/ssg.js +11 -23
- package/dist/server/ssg.js.map +1 -1
- package/package.json +4 -6
- package/dist/deploy/bun/runtime.d.ts +0 -11
- package/dist/deploy/bun/runtime.d.ts.map +0 -1
- package/dist/deploy/bun/runtime.js +0 -22
- package/dist/deploy/bun/runtime.js.map +0 -1
- package/dist/deploy/deno/runtime.d.ts +0 -11
- package/dist/deploy/deno/runtime.d.ts.map +0 -1
- package/dist/deploy/deno/runtime.js +0 -16
- package/dist/deploy/deno/runtime.js.map +0 -1
- package/dist/deploy/listen.d.ts +0 -20
- package/dist/deploy/listen.d.ts.map +0 -1
- package/dist/deploy/listen.js +0 -24
- package/dist/deploy/listen.js.map +0 -1
- package/dist/deploy/netlify/build.d.ts +0 -8
- package/dist/deploy/netlify/build.d.ts.map +0 -1
- package/dist/deploy/netlify/build.js +0 -52
- package/dist/deploy/netlify/build.js.map +0 -1
- package/dist/deploy/netlify/runtime.d.ts +0 -13
- package/dist/deploy/netlify/runtime.d.ts.map +0 -1
- package/dist/deploy/netlify/runtime.js +0 -24
- package/dist/deploy/netlify/runtime.js.map +0 -1
- package/dist/server/compress.d.ts +0 -15
- package/dist/server/compress.d.ts.map +0 -1
- package/dist/server/compress.js +0 -76
- package/dist/server/compress.js.map +0 -1
|
@@ -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,16 +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
|
|
5
|
-
* components.
|
|
4
|
+
* {@link AsyncBoundary} / {@link CatchBoundary} as components.
|
|
6
5
|
*
|
|
7
6
|
* Every export is itself a `'use client'` module, so a server component can
|
|
8
|
-
* render {@link
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* render {@link AsyncBoundary} directly — but the hook needs a client component. In a
|
|
8
|
+
* server component, read the same request data from `getRequestContext()` in
|
|
9
|
+
* `@rshono/core/server`.
|
|
11
10
|
*
|
|
12
11
|
* @packageDocumentation
|
|
13
12
|
*/
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
13
|
+
export { useNavigation, type NavigationRouter, type NavigationState } from './navigation.js';
|
|
14
|
+
export { AsyncBoundary, CatchBoundary, type AsyncBoundaryProps, type CatchBoundaryProps, type ErrorFallback } from './boundaries.js';
|
|
16
15
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/runtime/client.ts"],"names":[],"mappings":"AAAA
|
|
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,16 +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
|
|
5
|
-
* components.
|
|
4
|
+
* {@link AsyncBoundary} / {@link CatchBoundary} as components.
|
|
6
5
|
*
|
|
7
6
|
* Every export is itself a `'use client'` module, so a server component can
|
|
8
|
-
* render {@link
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* render {@link AsyncBoundary} directly — but the hook needs a client component. In a
|
|
8
|
+
* server component, read the same request data from `getRequestContext()` in
|
|
9
|
+
* `@rshono/core/server`.
|
|
11
10
|
*
|
|
12
11
|
* @packageDocumentation
|
|
13
12
|
*/
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
13
|
+
export { useNavigation } from './navigation.js';
|
|
14
|
+
export { AsyncBoundary, CatchBoundary } from './boundaries.js';
|
|
16
15
|
//# sourceMappingURL=client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/runtime/client.ts"],"names":[],"mappings":"AAAA
|
|
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"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The request context: {@link
|
|
2
|
+
* The request context: {@link getRequestContext} and the {@link RequestContext} wrapper it returns,
|
|
3
3
|
* the {@link redirect} / {@link notFound} control-flow helpers, and the
|
|
4
4
|
* {@link onServerError} reporting funnel — plus the `@internal` plumbing that binds
|
|
5
5
|
* a request to the async context in the first place.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* here is safe in a `'use client'` module — those run in the browser, with no bound
|
|
10
10
|
* request context.
|
|
11
11
|
*/
|
|
12
|
-
import type { Context, Env
|
|
12
|
+
import type { Context, Env } from 'hono';
|
|
13
13
|
import type { CookieOptions } from 'hono/utils/cookie';
|
|
14
14
|
/**
|
|
15
15
|
* HTTP status codes accepted by {@link redirect}.
|
|
@@ -22,21 +22,21 @@ import type { CookieOptions } from 'hono/utils/cookie';
|
|
|
22
22
|
export type RedirectStatus = 301 | 302 | 303 | 307 | 308;
|
|
23
23
|
/**
|
|
24
24
|
* Runs `fn` with the given Hono {@link Context} bound as the ambient request
|
|
25
|
-
* context, so that {@link
|
|
25
|
+
* context, so that {@link getRequestContext} resolves to it anywhere in the call tree.
|
|
26
26
|
*
|
|
27
27
|
* Framework internal — the request handler wraps every render and action in
|
|
28
|
-
* this. Application code should reach for {@link
|
|
28
|
+
* this. Application code should reach for {@link getRequestContext} instead.
|
|
29
29
|
*
|
|
30
30
|
* @internal
|
|
31
31
|
*/
|
|
32
32
|
export declare function runWithContext<T>(c: Context, fn: () => T): T;
|
|
33
33
|
/**
|
|
34
34
|
* Reads the matched route params, returning an empty object when there is no
|
|
35
|
-
* active route match (rather than throwing)
|
|
36
|
-
*
|
|
35
|
+
* active route match (rather than throwing), so the fallback behaviour stays in
|
|
36
|
+
* one place.
|
|
37
37
|
*
|
|
38
|
-
* Framework internal —
|
|
39
|
-
* `
|
|
38
|
+
* Framework internal — the request renderer calls this to build a page's `params`
|
|
39
|
+
* prop. Read them from that prop (or `ctx.raw.req.param()` outside a page) instead.
|
|
40
40
|
*
|
|
41
41
|
* @internal
|
|
42
42
|
*/
|
|
@@ -51,7 +51,7 @@ export declare function readParams(c: Context): Record<string, string>;
|
|
|
51
51
|
* dictate the origin of every absolute URL the app builds — canonical tags, emails, redirects — and
|
|
52
52
|
* poison a shared cache with them. So the default is to ignore them entirely.
|
|
53
53
|
*
|
|
54
|
-
* Framework internal — prefer {@link
|
|
54
|
+
* Framework internal — prefer {@link RequestContext.url}, which caches the result per request.
|
|
55
55
|
*
|
|
56
56
|
* @internal
|
|
57
57
|
*/
|
|
@@ -59,49 +59,52 @@ export declare function publicUrl(c: Context): URL;
|
|
|
59
59
|
/**
|
|
60
60
|
* The environment available to a request: Cloudflare/Workers `Bindings` merged
|
|
61
61
|
* with process env vars. Values not declared in `Bindings` are typed as
|
|
62
|
-
* `string | undefined`. See {@link
|
|
62
|
+
* `string | undefined`. See {@link RequestContext.env}.
|
|
63
63
|
*/
|
|
64
64
|
export type EnvVars<E extends Env> = E['Bindings'] & Record<string, string | undefined>;
|
|
65
65
|
/**
|
|
66
66
|
* Ergonomic, read-mostly wrapper around Hono's {@link Context} for use inside
|
|
67
67
|
* server components and server actions.
|
|
68
68
|
*
|
|
69
|
-
* Obtain one with {@link
|
|
69
|
+
* Obtain one with {@link getRequestContext}, or — in a page component — take it straight
|
|
70
70
|
* off the `ctx` prop, which is this same object. Never construct it yourself. One
|
|
71
71
|
* instance is reused for the lifetime of a request, so its lazy getters
|
|
72
|
-
* ({@link
|
|
72
|
+
* ({@link RequestContext.url}, {@link RequestContext.env}) are computed at most once.
|
|
73
73
|
*
|
|
74
74
|
* @typeParam E - The Hono {@link Env} describing this app's `Bindings` and
|
|
75
|
-
* `Variables`, so {@link
|
|
75
|
+
* `Variables`, so {@link RequestContext.var} and {@link RequestContext.env} stay typed.
|
|
76
76
|
*
|
|
77
77
|
* @example
|
|
78
78
|
* ```tsx
|
|
79
|
-
* import {
|
|
79
|
+
* import { getRequestContext } from '@rshono/core/server';
|
|
80
80
|
*
|
|
81
81
|
* export default async function Whoami() {
|
|
82
|
-
* const ctx =
|
|
82
|
+
* const ctx = getRequestContext();
|
|
83
83
|
* const session = ctx.cookies.get('session');
|
|
84
84
|
* return <p>{ctx.url.pathname} — {session ?? 'anonymous'}</p>;
|
|
85
85
|
* }
|
|
86
86
|
* ```
|
|
87
87
|
*/
|
|
88
|
-
export declare class
|
|
88
|
+
export declare class RequestContext<E extends Env = Env> {
|
|
89
89
|
#private;
|
|
90
90
|
constructor(c: Context<E>);
|
|
91
91
|
/**
|
|
92
|
-
* The underlying Hono {@link Context}. Escape hatch for anything this wrapper does not expose
|
|
92
|
+
* The underlying Hono {@link Context}. Escape hatch for anything this wrapper does not expose — and
|
|
93
|
+
* deliberately most things. `req`, `method`, `params` and a `header()` setter used to sit on this class
|
|
94
|
+
* as one-line pass-throughs to `c.req`, `c.req.method`, `c.req.param()` and `c.header()`; they are
|
|
95
|
+
* reachable through here (`ctx.raw.req`, `ctx.raw.header(…)`) and adding no name of their own is the
|
|
96
|
+
* point. What stays below is what this wrapper actually *does*: a proxy-aware cached URL, an env that
|
|
97
|
+
* merges runtime bindings over process env, and cookies without a second import.
|
|
93
98
|
*
|
|
94
99
|
* A getter over a private field rather than a plain property, so it is not an *own enumerable*
|
|
95
100
|
* one — which matters more than it looks. React's diagnostic for a value that cannot be sent to a
|
|
96
101
|
* client component (`describeObjectForErrorMessage`) walks `Object.keys` recursively with no depth
|
|
97
102
|
* limit and no cycle guard, and the Hono context graph reaches the socket and the whole server
|
|
98
|
-
* through `req.raw` and `env`. While this was a plain property, passing a `
|
|
103
|
+
* through `req.raw` and `env`. While this was a plain property, passing a `RequestContext` to a `'use client'`
|
|
99
104
|
* component blew the stack *inside that message builder* — so React's actual, accurate "you cannot
|
|
100
105
|
* pass this" error never got printed. Hidden from `Object.keys`, the walk stops here.
|
|
101
106
|
*/
|
|
102
107
|
get raw(): Context<E>;
|
|
103
|
-
/** The parsed Hono request (`c.req`) — headers, body parsing, param access, etc. */
|
|
104
|
-
get req(): HonoRequest;
|
|
105
108
|
/**
|
|
106
109
|
* The browser-facing request URL, proxy-header aware (see {@link publicUrl}) —
|
|
107
110
|
* read `url.pathname`, `url.searchParams` and the rest off it. Parsed once and
|
|
@@ -109,13 +112,6 @@ export declare class Ctx<E extends Env = Env> {
|
|
|
109
112
|
* it as read-only for that reason.
|
|
110
113
|
*/
|
|
111
114
|
get url(): URL;
|
|
112
|
-
/** The HTTP method of the request, e.g. `GET` or `POST`. */
|
|
113
|
-
get method(): string;
|
|
114
|
-
/**
|
|
115
|
-
* Matched route params, e.g. `{ id }` for a `/users/[id]` route. Returns an
|
|
116
|
-
* empty object when there is no active route match (rather than throwing).
|
|
117
|
-
*/
|
|
118
|
-
get params(): Record<string, string>;
|
|
119
115
|
/**
|
|
120
116
|
* Typed variables set by middleware via `c.set('user', …)`, read here as
|
|
121
117
|
* `ctx.var.user`. Type them by parameterising this class's {@link Env}.
|
|
@@ -125,17 +121,15 @@ export declare class Ctx<E extends Env = Env> {
|
|
|
125
121
|
* Environment for the request: process env vars merged with runtime bindings
|
|
126
122
|
* (bindings win on conflict). Computed once and cached.
|
|
127
123
|
*
|
|
128
|
-
* @example `const key =
|
|
124
|
+
* @example `const key = getRequestContext().env.STRIPE_SECRET_KEY;`
|
|
129
125
|
*/
|
|
130
126
|
get env(): EnvVars<E>;
|
|
131
|
-
/** Sets a response header. Thin pass-through to `c.header(name, value)`. */
|
|
132
|
-
header(name: string, value: string): void;
|
|
133
127
|
/**
|
|
134
128
|
* Read and write request/response cookies.
|
|
135
129
|
*
|
|
136
130
|
* @example
|
|
137
131
|
* ```ts
|
|
138
|
-
* const ctx =
|
|
132
|
+
* const ctx = getRequestContext();
|
|
139
133
|
* ctx.cookies.get('session'); // string | undefined
|
|
140
134
|
* ctx.cookies.set('session', id, { httpOnly: true, sameSite: 'Lax', path: '/' });
|
|
141
135
|
* ctx.cookies.delete('session', { path: '/' });
|
|
@@ -153,7 +147,7 @@ export declare class Ctx<E extends Env = Env> {
|
|
|
153
147
|
};
|
|
154
148
|
}
|
|
155
149
|
/**
|
|
156
|
-
* Returns the {@link
|
|
150
|
+
* Returns the {@link RequestContext} for the current request.
|
|
157
151
|
*
|
|
158
152
|
* This is the primary entry point for reading request data from a server
|
|
159
153
|
* component or server action — the URL, cookies, params, env, and middleware
|
|
@@ -164,7 +158,7 @@ export declare class Ctx<E extends Env = Env> {
|
|
|
164
158
|
* import is for everywhere else: a nested server component, or a `'use server'`
|
|
165
159
|
* action module — neither of which receives props from the framework.
|
|
166
160
|
*
|
|
167
|
-
* @typeParam E - The app's Hono {@link Env}, to type {@link
|
|
161
|
+
* @typeParam E - The app's Hono {@link Env}, to type {@link RequestContext.var} and {@link RequestContext.env}.
|
|
168
162
|
* @throws If called at module load, where there is no ambient context to resolve.
|
|
169
163
|
* @throws If called while prerendering a `render: 'static'` route, which has no
|
|
170
164
|
* per-request context at build time — mark the route `render: 'dynamic'` instead.
|
|
@@ -172,15 +166,15 @@ export declare class Ctx<E extends Env = Env> {
|
|
|
172
166
|
* @example
|
|
173
167
|
* ```ts
|
|
174
168
|
* 'use server';
|
|
175
|
-
* import {
|
|
169
|
+
* import { getRequestContext, redirect } from '@rshono/core/server';
|
|
176
170
|
*
|
|
177
171
|
* export async function login(form: FormData) {
|
|
178
|
-
*
|
|
172
|
+
* getRequestContext().cookies.set('session', String(form.get('email')), { httpOnly: true });
|
|
179
173
|
* redirect('/dashboard');
|
|
180
174
|
* }
|
|
181
175
|
* ```
|
|
182
176
|
*/
|
|
183
|
-
export declare function
|
|
177
|
+
export declare function getRequestContext<E extends Env = Env>(): RequestContext<E>;
|
|
184
178
|
/**
|
|
185
179
|
* Redirects the request to `location` by throwing a control signal that the
|
|
186
180
|
* framework catches and turns into an HTTP redirect response.
|
|
@@ -195,7 +189,7 @@ export declare function getContext<E extends Env = Env>(): Ctx<E>;
|
|
|
195
189
|
*
|
|
196
190
|
* @example
|
|
197
191
|
* ```ts
|
|
198
|
-
* const session =
|
|
192
|
+
* const session = getRequestContext().cookies.get('session');
|
|
199
193
|
* if (!session) redirect('/login');
|
|
200
194
|
* // session is defined below this line
|
|
201
195
|
* ```
|
|
@@ -209,9 +203,11 @@ export declare function redirect(location: string, status?: RedirectStatus): nev
|
|
|
209
203
|
*
|
|
210
204
|
* @example
|
|
211
205
|
* ```tsx
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
206
|
+
* export default async function Page({ params }: PageProps<'/users/:id'>) {
|
|
207
|
+
* const user = await db.user.find(params.id);
|
|
208
|
+
* if (!user) notFound();
|
|
209
|
+
* return <Profile user={user} />; // user is non-null here
|
|
210
|
+
* }
|
|
215
211
|
* ```
|
|
216
212
|
*/
|
|
217
213
|
export declare function notFound(): never;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"AACA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"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"}
|