@rshono/core 1.0.0-rc.12 → 1.0.0-rc.13
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/bin/rshono.mjs +3 -4
- package/dist/builder/env-shadow-loader.cjs +5 -5
- package/dist/builder/page-files.js +5 -5
- package/dist/builder/page-files.js.map +1 -1
- package/dist/builder/public-env.d.ts +5 -4
- package/dist/builder/public-env.d.ts.map +1 -1
- package/dist/builder/public-env.js +5 -4
- package/dist/builder/public-env.js.map +1 -1
- package/dist/builder/rspack-config.d.ts +5 -11
- package/dist/builder/rspack-config.d.ts.map +1 -1
- package/dist/builder/rspack-config.js +28 -50
- package/dist/builder/rspack-config.js.map +1 -1
- package/dist/cli/build.js +2 -2
- package/dist/cli/build.js.map +1 -1
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/dev.js +30 -40
- package/dist/cli/dev.js.map +1 -1
- package/dist/cli/index.js +4 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/start.d.ts.map +1 -1
- package/dist/cli/start.js +2 -3
- package/dist/cli/start.js.map +1 -1
- package/dist/config.d.ts +28 -31
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/deploy/aws-lambda/runtime.d.ts +4 -6
- package/dist/deploy/aws-lambda/runtime.d.ts.map +1 -1
- package/dist/deploy/aws-lambda/runtime.js +5 -8
- package/dist/deploy/aws-lambda/runtime.js.map +1 -1
- package/dist/deploy/build-marker.d.ts +3 -5
- package/dist/deploy/build-marker.d.ts.map +1 -1
- package/dist/deploy/build-marker.js +3 -5
- package/dist/deploy/build-marker.js.map +1 -1
- package/dist/deploy/cloudflare/build.d.ts.map +1 -1
- package/dist/deploy/cloudflare/build.js +6 -10
- package/dist/deploy/cloudflare/build.js.map +1 -1
- package/dist/deploy/cloudflare/runtime.d.ts +2 -5
- package/dist/deploy/cloudflare/runtime.d.ts.map +1 -1
- package/dist/deploy/cloudflare/runtime.js +19 -32
- package/dist/deploy/cloudflare/runtime.js.map +1 -1
- package/dist/deploy/contract.d.ts +25 -42
- package/dist/deploy/contract.d.ts.map +1 -1
- package/dist/deploy/contract.js.map +1 -1
- package/dist/deploy/filesystem.d.ts +3 -5
- package/dist/deploy/filesystem.d.ts.map +1 -1
- package/dist/deploy/filesystem.js +7 -11
- package/dist/deploy/filesystem.js.map +1 -1
- package/dist/deploy/node/runtime.d.ts +4 -5
- package/dist/deploy/node/runtime.d.ts.map +1 -1
- package/dist/deploy/node/runtime.js +9 -15
- package/dist/deploy/node/runtime.js.map +1 -1
- package/dist/deploy/presets.d.ts +19 -29
- package/dist/deploy/presets.d.ts.map +1 -1
- package/dist/deploy/presets.js +18 -25
- package/dist/deploy/presets.js.map +1 -1
- package/dist/deploy/vercel/build.d.ts.map +1 -1
- package/dist/deploy/vercel/build.js +9 -12
- package/dist/deploy/vercel/build.js.map +1 -1
- package/dist/deploy/vercel/runtime.d.ts +4 -7
- package/dist/deploy/vercel/runtime.d.ts.map +1 -1
- package/dist/deploy/vercel/runtime.js +4 -7
- package/dist/deploy/vercel/runtime.js.map +1 -1
- package/dist/index.d.ts +13 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -12
- package/dist/index.js.map +1 -1
- package/dist/router.d.ts +65 -93
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +2 -5
- package/dist/router.js.map +1 -1
- package/dist/runtime/boundaries.d.ts +24 -30
- package/dist/runtime/boundaries.d.ts.map +1 -1
- package/dist/runtime/boundaries.js +15 -22
- package/dist/runtime/boundaries.js.map +1 -1
- package/dist/runtime/client.d.ts +16 -7
- package/dist/runtime/client.d.ts.map +1 -1
- package/dist/runtime/client.js +16 -7
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/context.d.ts +89 -118
- package/dist/runtime/context.d.ts.map +1 -1
- package/dist/runtime/context.js +107 -167
- package/dist/runtime/context.js.map +1 -1
- package/dist/runtime/control.js +3 -3
- package/dist/runtime/control.js.map +1 -1
- package/dist/runtime/dev-protocol.d.ts +4 -8
- package/dist/runtime/dev-protocol.d.ts.map +1 -1
- package/dist/runtime/dev-protocol.js.map +1 -1
- package/dist/runtime/entry.client.js +69 -105
- package/dist/runtime/entry.client.js.map +1 -1
- package/dist/runtime/entry.rsc.d.ts +5 -6
- package/dist/runtime/entry.rsc.d.ts.map +1 -1
- package/dist/runtime/entry.rsc.js +67 -122
- package/dist/runtime/entry.rsc.js.map +1 -1
- package/dist/runtime/entry.ssr.d.ts +7 -12
- package/dist/runtime/entry.ssr.d.ts.map +1 -1
- package/dist/runtime/entry.ssr.js +13 -24
- package/dist/runtime/entry.ssr.js.map +1 -1
- package/dist/runtime/flight-inject.d.ts +10 -17
- package/dist/runtime/flight-inject.d.ts.map +1 -1
- package/dist/runtime/flight-inject.js +35 -53
- package/dist/runtime/flight-inject.js.map +1 -1
- package/dist/runtime/hot-update.d.ts +20 -27
- package/dist/runtime/hot-update.d.ts.map +1 -1
- package/dist/runtime/hot-update.js +17 -23
- package/dist/runtime/hot-update.js.map +1 -1
- package/dist/runtime/navigation.d.ts +14 -19
- package/dist/runtime/navigation.d.ts.map +1 -1
- package/dist/runtime/navigation.js +9 -13
- package/dist/runtime/navigation.js.map +1 -1
- package/dist/runtime/request.d.ts +4 -6
- package/dist/runtime/request.d.ts.map +1 -1
- package/dist/runtime/request.js +2 -3
- package/dist/runtime/request.js.map +1 -1
- package/dist/runtime/server.d.ts +18 -10
- package/dist/runtime/server.d.ts.map +1 -1
- package/dist/runtime/server.js +21 -19
- package/dist/runtime/server.js.map +1 -1
- package/dist/server/headers.d.ts +8 -15
- package/dist/server/headers.d.ts.map +1 -1
- package/dist/server/headers.js +8 -15
- 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 +6 -9
- package/dist/server/load-config.js.map +1 -1
- package/dist/server/prerendered.d.ts +30 -43
- package/dist/server/prerendered.d.ts.map +1 -1
- package/dist/server/prerendered.js +20 -29
- package/dist/server/prerendered.js.map +1 -1
- package/dist/server/server-config.d.ts +13 -28
- package/dist/server/server-config.d.ts.map +1 -1
- package/dist/server/server-config.js +5 -10
- package/dist/server/server-config.js.map +1 -1
- package/dist/server/shutdown.d.ts +2 -3
- package/dist/server/shutdown.d.ts.map +1 -1
- package/dist/server/shutdown.js +2 -3
- package/dist/server/shutdown.js.map +1 -1
- package/dist/server/ssg.d.ts +3 -6
- package/dist/server/ssg.d.ts.map +1 -1
- package/dist/server/ssg.js +11 -20
- package/dist/server/ssg.js.map +1 -1
- package/package.json +1 -1
package/dist/runtime/context.js
CHANGED
|
@@ -1,51 +1,34 @@
|
|
|
1
|
+
// `__RSHONO_CONFIG__` is a global const, and an `import` cannot bring one into scope — a path reference is
|
|
2
|
+
// the only way to reach it, which is the whole reason that file is separate. See its header.
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
|
|
1
4
|
/// <reference path="../types/rshono-config.d.ts" />
|
|
2
5
|
/**
|
|
3
|
-
* The request context: {@link getRequestContext} and the {@link RequestContext}
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* a request to the async context in the first place.
|
|
6
|
+
* The request context: {@link getRequestContext} and the {@link RequestContext} it returns, the
|
|
7
|
+
* {@link redirect} / {@link notFound} control-flow helpers, and the {@link onServerError} reporting
|
|
8
|
+
* funnel — plus the `@internal` plumbing that binds a request to the async context.
|
|
7
9
|
*
|
|
8
|
-
* The public half
|
|
9
|
-
*
|
|
10
|
-
* here is safe in a `'use client'` module — those run in the browser, with no bound
|
|
11
|
-
* request context.
|
|
10
|
+
* The public half is re-exported by `runtime/server.ts`, which is what `@rshono/core/server` resolves
|
|
11
|
+
* to; an app imports that.
|
|
12
12
|
*/
|
|
13
13
|
import { deleteCookie, getCookie, setCookie } from 'hono/cookie';
|
|
14
14
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
15
15
|
import { NotFoundSignal, RedirectSignal } from './control.js';
|
|
16
16
|
const contextStorage = new AsyncLocalStorage();
|
|
17
|
-
/** One {@link RequestContext} per Hono {@link Context}, so repeated `getRequestContext()` calls
|
|
17
|
+
/** One {@link RequestContext} per Hono {@link Context}, so repeated `getRequestContext()` calls share its lazy getters. */
|
|
18
18
|
const wrappers = new WeakMap();
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*
|
|
22
|
-
* A `WeakSet` keyed on the Hono {@link Context} rather than a field on {@link RequestContext},
|
|
23
|
-
* so marking a request costs nothing for the pages that never read their context: the wrapper is
|
|
24
|
-
* built lazily by {@link getRequestContext} and this must not be what forces it into existence.
|
|
25
|
-
*/
|
|
19
|
+
// Keyed on the Hono context rather than held as a field, so marking a request never forces the lazily
|
|
20
|
+
// built `RequestContext` wrapper into existence.
|
|
26
21
|
const rendering = new WeakSet();
|
|
27
22
|
/**
|
|
28
23
|
* Marks the request as having entered its page render, which is what makes
|
|
29
24
|
* {@link RequestContext.setHeader} and `ctx.cookies.set()` start throwing.
|
|
30
25
|
*
|
|
31
|
-
* Framework internal — `renderComponent` calls this immediately before handing the page to React.
|
|
32
|
-
* Everything that legitimately writes to the response (middleware, a `'use server'` action, an
|
|
33
|
-
* endpoint route) has already run by then, so none of them are affected.
|
|
34
|
-
*
|
|
35
26
|
* @internal
|
|
36
27
|
*/
|
|
37
28
|
export function beginPageRender(c) {
|
|
38
29
|
rendering.add(c);
|
|
39
30
|
}
|
|
40
|
-
/**
|
|
41
|
-
* The shared explanation for a response mutation that arrived too late, thrown by
|
|
42
|
-
* {@link RequestContext.setHeader} and the `cookies` writers.
|
|
43
|
-
*
|
|
44
|
-
* Refusing beats the alternative, which was silent *and* inconsistent: a page setting a cookie got it
|
|
45
|
-
* on a full page load and lost it on a soft navigation, because the flight stream's response head is
|
|
46
|
-
* committed before the page component's first line runs. Nothing inside the render can fix that, so
|
|
47
|
-
* the message says where the write does belong instead.
|
|
48
|
-
*/
|
|
31
|
+
/** The shared refusal for a response write that arrived too late — the message names where it belongs instead. */
|
|
49
32
|
function tooLateToWrite(call) {
|
|
50
33
|
throw new Error(`[rshono] ${call} was called while rendering a page, which is too late to affect the response. ` +
|
|
51
34
|
'A page streams, so its response head is already committed by the time the component runs — the ' +
|
|
@@ -53,38 +36,24 @@ function tooLateToWrite(call) {
|
|
|
53
36
|
"'use server' action instead; or, in middleware and { type: 'endpoint' } routes — which are handed " +
|
|
54
37
|
"Hono's `c` directly and run outside the request context — with `c.header(…)` / `setCookie(c, …)`.");
|
|
55
38
|
}
|
|
56
|
-
/** The shared
|
|
39
|
+
/** The shared refusal for a Hono `Context` member a page has no way to use. See the stubs on {@link RequestContext}. */
|
|
57
40
|
function notOnContext(call, instead) {
|
|
58
41
|
throw new Error(`[rshono] ctx.${call} does not exist. A page returns JSX and the framework builds the response from it, ` +
|
|
59
42
|
`so Hono's response builders have nothing to return to. ${instead}`);
|
|
60
43
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
* Enumerating it crosses into the host environment, which made the spread (~20µs) by far the most
|
|
65
|
-
* expensive thing {@link RequestContext.env} did, once per request that touched it. Lazily rather
|
|
66
|
-
* than at module load, because `loadEnvFiles()` runs *after* this module is imported and an eager
|
|
67
|
-
* copy would miss everything from `.env`. The trade-off: a `process.env` mutation after the first
|
|
68
|
-
* `ctx.env` read is not picked up.
|
|
69
|
-
*/
|
|
44
|
+
// Snapshotted rather than spread per request: enumerating `process.env` crosses into the host
|
|
45
|
+
// environment (~20µs). Lazily, because `loadEnvFiles()` runs after this module is imported — so a
|
|
46
|
+
// mutation after the first `ctx.env` read is not picked up.
|
|
70
47
|
let envSnapshot;
|
|
71
48
|
function processEnv() {
|
|
72
49
|
return (envSnapshot ??= typeof process !== 'undefined' && process.env ? { ...process.env } : {});
|
|
73
50
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
* handling real requests. `build.ts` sets `RSHONO_PRERENDER` before importing the app bundle, which
|
|
77
|
-
* inlines its own copy of this module — so `process.env` is what crosses that boundary, not a
|
|
78
|
-
* module-level flag. {@link getRequestContext} reads it to fail loudly instead of baking synthetic
|
|
79
|
-
* build-time values (a `localhost` URL, no cookies, build env) into the prerendered page.
|
|
80
|
-
*/
|
|
51
|
+
// Set by `build.ts` before it imports the app bundle, which inlines its own copy of this module — so
|
|
52
|
+
// `process.env` is what crosses that boundary rather than a module-level flag.
|
|
81
53
|
const prerendering = typeof process !== 'undefined' && !!process.env?.RSHONO_PRERENDER;
|
|
82
54
|
/**
|
|
83
|
-
* Runs `fn` with the
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* Framework internal — the request handler wraps every render and action in
|
|
87
|
-
* this. Application code should reach for {@link getRequestContext} instead.
|
|
55
|
+
* Runs `fn` with `c` bound as the ambient request context, so {@link getRequestContext} resolves to it
|
|
56
|
+
* anywhere in the call tree.
|
|
88
57
|
*
|
|
89
58
|
* @internal
|
|
90
59
|
*/
|
|
@@ -94,9 +63,6 @@ export function runWithContext(c, fn) {
|
|
|
94
63
|
/**
|
|
95
64
|
* The matched route params, or an empty object when there is no active match.
|
|
96
65
|
*
|
|
97
|
-
* Framework internal — the request renderer calls this to build a page's `params` prop, and
|
|
98
|
-
* {@link RequestContext.params} caches it. Read them from that prop, or from `ctx.params`.
|
|
99
|
-
*
|
|
100
66
|
* @internal
|
|
101
67
|
*/
|
|
102
68
|
export function readParams(c) {
|
|
@@ -112,25 +78,24 @@ function firstForwardedValue(header) {
|
|
|
112
78
|
const first = header?.split(',')[0]?.trim();
|
|
113
79
|
return first || undefined;
|
|
114
80
|
}
|
|
115
|
-
// DefinePlugin inlines
|
|
116
|
-
// `@rshono/core/server` entry
|
|
117
|
-
// Read through `typeof` so that degrades to the safe answer — don't trust — instead of a ReferenceError.
|
|
81
|
+
// Read through `typeof`: DefinePlugin inlines this into the server bundle, but the module is also the
|
|
82
|
+
// public `@rshono/core/server` entry, which tooling can load without one. Absent means don't trust.
|
|
118
83
|
const trustProxy = typeof __RSHONO_CONFIG__ !== 'undefined' && __RSHONO_CONFIG__.trustProxy;
|
|
119
84
|
/**
|
|
120
|
-
*
|
|
85
|
+
* The browser-facing {@link URL} for a request, resolved from Hono's {@link Context} — a fresh
|
|
86
|
+
* instance per call.
|
|
121
87
|
*
|
|
122
|
-
* `c.req.url`
|
|
123
|
-
* `X-Forwarded-Host` /
|
|
124
|
-
* `rshono.config.ts
|
|
125
|
-
*
|
|
126
|
-
* the app builds, and poison a shared cache with it.
|
|
88
|
+
* `c.req.url` is the internal address the server was reached on, which is wrong behind a proxy;
|
|
89
|
+
* `X-Forwarded-Host` / `-Proto` correct it, but only when `trustProxy` is enabled in
|
|
90
|
+
* `rshono.config.ts` — they are client-supplied, so trusting them unconditionally would let anyone
|
|
91
|
+
* dictate the origin of every absolute URL the app builds.
|
|
127
92
|
*
|
|
128
|
-
* This is the form for **middleware**, which is handed `c` and runs outside the request context
|
|
129
|
-
*
|
|
130
|
-
* value cached per request.
|
|
93
|
+
* This is the form for **middleware**, which is handed `c` and runs outside the request context — and
|
|
94
|
+
* so the way to give Hono's own middleware the origin the browser actually used. In a server component
|
|
95
|
+
* or action, prefer {@link RequestContext.url}, the same value cached per request.
|
|
131
96
|
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
97
|
+
* @param c - The Hono {@link Context} for the request.
|
|
98
|
+
* @returns The browser-facing URL — proxy-corrected under `trustProxy`, `c.req.url` otherwise.
|
|
134
99
|
*
|
|
135
100
|
* @example
|
|
136
101
|
* ```ts
|
|
@@ -141,11 +106,6 @@ const trustProxy = typeof __RSHONO_CONFIG__ !== 'undefined' && __RSHONO_CONFIG__
|
|
|
141
106
|
* server.use(csrf({ origin: (origin, c) => origin === publicUrl(c).origin }));
|
|
142
107
|
* ```
|
|
143
108
|
*
|
|
144
|
-
* A fresh instance per call, so mutating it disturbs nothing else.
|
|
145
|
-
*
|
|
146
|
-
* @param c - The Hono {@link Context} for the request.
|
|
147
|
-
* @returns The browser-facing URL — proxy-corrected under `trustProxy`, `c.req.url` otherwise.
|
|
148
|
-
*
|
|
149
109
|
* @see {@link https://www.rshono.com/docs/configuration#proxy-headers | Docs — proxy headers}
|
|
150
110
|
*/
|
|
151
111
|
export function publicUrl(c) {
|
|
@@ -153,31 +113,27 @@ export function publicUrl(c) {
|
|
|
153
113
|
if (!trustProxy)
|
|
154
114
|
return url;
|
|
155
115
|
const forwardedHost = firstForwardedValue(c.req.header('x-forwarded-host'));
|
|
156
|
-
// Parsed
|
|
157
|
-
// the new value has none — leaving the internal port on the public URL (`example.com:3000`).
|
|
116
|
+
// Parsed, not assigned to `url.host`: that setter keeps the existing port when the new value has none.
|
|
158
117
|
const forwarded = forwardedHost ? URL.parse(`http://${forwardedHost}`) : null;
|
|
159
118
|
if (forwarded) {
|
|
160
119
|
url.hostname = forwarded.hostname;
|
|
161
|
-
url.port = forwarded.port;
|
|
120
|
+
url.port = forwarded.port;
|
|
162
121
|
}
|
|
163
|
-
//
|
|
164
|
-
// sending junk, or a client trying its luck) leaves the scheme alone.
|
|
122
|
+
// Only the two schemes a browser could have requested; anything else leaves the scheme alone.
|
|
165
123
|
const forwardedProto = firstForwardedValue(c.req.header('x-forwarded-proto'));
|
|
166
124
|
if (forwardedProto === 'http' || forwardedProto === 'https')
|
|
167
125
|
url.protocol = forwardedProto;
|
|
168
126
|
return url;
|
|
169
127
|
}
|
|
170
128
|
/**
|
|
171
|
-
*
|
|
172
|
-
* server components and server actions.
|
|
129
|
+
* Read-mostly wrapper around Hono's {@link Context}, for server components and server actions.
|
|
173
130
|
*
|
|
174
|
-
* Obtain one with {@link getRequestContext}, or
|
|
175
|
-
*
|
|
176
|
-
* instance is reused for the lifetime of a request, so its lazy getters
|
|
131
|
+
* Obtain one with {@link getRequestContext}, or take it off a page's `ctx` prop — the same object.
|
|
132
|
+
* Never construct it yourself. One instance is reused for the whole request, so its lazy getters
|
|
177
133
|
* ({@link RequestContext.url}, {@link RequestContext.env}) are computed at most once.
|
|
178
134
|
*
|
|
179
|
-
* @typeParam E - The Hono {@link Env} describing this app's `Bindings` and
|
|
180
|
-
*
|
|
135
|
+
* @typeParam E - The Hono {@link Env} describing this app's `Bindings` and `Variables`, so
|
|
136
|
+
* {@link RequestContext.var} and {@link RequestContext.env} stay typed.
|
|
181
137
|
*
|
|
182
138
|
* @example
|
|
183
139
|
* ```tsx
|
|
@@ -199,8 +155,8 @@ export class RequestContext {
|
|
|
199
155
|
#env;
|
|
200
156
|
#params;
|
|
201
157
|
/**
|
|
202
|
-
*
|
|
203
|
-
*
|
|
158
|
+
* One instance is created per request and handed out by {@link getRequestContext} or the `ctx` page
|
|
159
|
+
* prop. Application code never calls this.
|
|
204
160
|
*
|
|
205
161
|
* @internal
|
|
206
162
|
*/
|
|
@@ -208,12 +164,12 @@ export class RequestContext {
|
|
|
208
164
|
this.#raw = c;
|
|
209
165
|
}
|
|
210
166
|
/**
|
|
211
|
-
* The underlying Hono {@link Context} — the escape hatch for what this wrapper does not expose,
|
|
212
|
-
*
|
|
167
|
+
* The underlying Hono {@link Context} — the escape hatch for what this wrapper does not expose, such
|
|
168
|
+
* as `executionCtx.waitUntil()` on Workers.
|
|
213
169
|
*
|
|
214
|
-
* Its response builders (`redirect`, `
|
|
215
|
-
*
|
|
216
|
-
*
|
|
170
|
+
* Its response builders (`redirect`, `json`, `body`, `status`, …) still do nothing from inside a
|
|
171
|
+
* page: reaching them through here bypasses the errors the stubs on this class throw, it does not
|
|
172
|
+
* make them work.
|
|
217
173
|
*
|
|
218
174
|
* @example
|
|
219
175
|
* ```ts
|
|
@@ -222,13 +178,10 @@ export class RequestContext {
|
|
|
222
178
|
*
|
|
223
179
|
* @see {@link https://hono.dev/docs/api/context | Hono — Context}
|
|
224
180
|
*/
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
// message builder, so React's accurate "you cannot pass this" error never got printed. Every member
|
|
230
|
-
// here is a getter or method for the same reason; `cookies` is the one own enumerable property, and
|
|
231
|
-
// it is a shallow object of four functions.
|
|
181
|
+
// Every member here is a getter or method so that none is *own enumerable*: React's "you cannot pass
|
|
182
|
+
// this to a client component" diagnostic walks `Object.keys` recursively with no cycle guard, and the
|
|
183
|
+
// Hono context graph reaches the socket through `req.raw`. `cookies` is the one own property, and it
|
|
184
|
+
// is a shallow object of four functions.
|
|
232
185
|
get hono() {
|
|
233
186
|
return this.#raw;
|
|
234
187
|
}
|
|
@@ -237,9 +190,8 @@ export class RequestContext {
|
|
|
237
190
|
* {@link Context.req}, unwrapped, so `ctx.req.header('authorization')` rather than
|
|
238
191
|
* `ctx.hono.req.header(…)`.
|
|
239
192
|
*
|
|
240
|
-
* Reads only
|
|
241
|
-
*
|
|
242
|
-
* request is a well-worn source of confusion.
|
|
193
|
+
* Reads only; setting a *response* header is {@link RequestContext.setHeader}, deliberately spelled
|
|
194
|
+
* differently.
|
|
243
195
|
*
|
|
244
196
|
* @example
|
|
245
197
|
* ```ts
|
|
@@ -255,32 +207,33 @@ export class RequestContext {
|
|
|
255
207
|
return this.#raw.req;
|
|
256
208
|
}
|
|
257
209
|
/**
|
|
258
|
-
* Matched route params for this request, e.g. `{ id: '42' }` for `/profile/:id`. Empty when
|
|
259
|
-
*
|
|
210
|
+
* Matched route params for this request, e.g. `{ id: '42' }` for `/profile/:id`. Empty when no route
|
|
211
|
+
* matched.
|
|
260
212
|
*
|
|
261
|
-
* A
|
|
262
|
-
*
|
|
263
|
-
*
|
|
213
|
+
* A page is handed the same record as its `params` prop, typed key-by-key from its route path, and
|
|
214
|
+
* that is the better read where it exists. This is for everywhere else — a nested server component,
|
|
215
|
+
* or a `'use server'` action.
|
|
264
216
|
*/
|
|
265
217
|
get params() {
|
|
266
218
|
return (this.#params ??= readParams(this.#raw));
|
|
267
219
|
}
|
|
268
220
|
/**
|
|
269
|
-
* The browser-facing request URL
|
|
270
|
-
*
|
|
271
|
-
* read within a request; treat it as read-only for that reason.
|
|
221
|
+
* The browser-facing request URL. Parsed once and cached, so every read within a request returns the
|
|
222
|
+
* same instance — treat it as read-only.
|
|
272
223
|
*
|
|
273
224
|
* `X-Forwarded-Host` / `-Proto` are honoured only when `trustProxy` is enabled in
|
|
274
225
|
* `rshono.config.ts`, since any client can send them.
|
|
275
226
|
*
|
|
227
|
+
* @example `const tab = getRequestContext().url.searchParams.get('tab');`
|
|
228
|
+
*
|
|
276
229
|
* @see {@link https://www.rshono.com/docs/configuration#proxy-headers | Docs — proxy headers}
|
|
277
230
|
*/
|
|
278
231
|
get url() {
|
|
279
232
|
return (this.#url ??= publicUrl(this.#raw));
|
|
280
233
|
}
|
|
281
234
|
/**
|
|
282
|
-
* Typed variables set by middleware via `c.set('user', …)`, read here as
|
|
283
|
-
*
|
|
235
|
+
* Typed variables set by middleware via `c.set('user', …)`, read here as `ctx.var.user`. Type them by
|
|
236
|
+
* parameterising this class's {@link Env}.
|
|
284
237
|
*
|
|
285
238
|
* @example
|
|
286
239
|
* ```ts
|
|
@@ -295,8 +248,8 @@ export class RequestContext {
|
|
|
295
248
|
return this.#raw.var;
|
|
296
249
|
}
|
|
297
250
|
/**
|
|
298
|
-
* Environment for the request: process env vars merged with runtime bindings
|
|
299
|
-
*
|
|
251
|
+
* Environment for the request: process env vars merged with runtime bindings, which win on conflict.
|
|
252
|
+
* Computed once and cached.
|
|
300
253
|
*
|
|
301
254
|
* @example `const key = getRequestContext().env.STRIPE_SECRET_KEY;`
|
|
302
255
|
*
|
|
@@ -307,7 +260,6 @@ export class RequestContext {
|
|
|
307
260
|
if (this.#env)
|
|
308
261
|
return this.#env;
|
|
309
262
|
const bindings = this.#raw.env;
|
|
310
|
-
// The snapshot is shared, so hand it back as-is when there are no bindings to merge over it.
|
|
311
263
|
return (this.#env = (bindings ? { ...processEnv(), ...bindings } : processEnv()));
|
|
312
264
|
}
|
|
313
265
|
/**
|
|
@@ -329,12 +281,12 @@ export class RequestContext {
|
|
|
329
281
|
/** Reads every cookie as a `{ name: value }` record. Safe anywhere, a page included. */
|
|
330
282
|
all: () => getCookie(this.#raw),
|
|
331
283
|
/**
|
|
332
|
-
* Sets a cookie on the response. See Hono's {@link CookieOptions} for `path`, `httpOnly`,
|
|
333
|
-
*
|
|
284
|
+
* Sets a cookie on the response. See Hono's {@link CookieOptions} for `path`, `httpOnly`, `maxAge`
|
|
285
|
+
* and the rest.
|
|
334
286
|
*
|
|
335
|
-
* **Throws inside a page render** —
|
|
336
|
-
*
|
|
337
|
-
* in middleware and endpoint routes.
|
|
287
|
+
* **Throws inside a page render** — a `Set-Cookie` is a special case of
|
|
288
|
+
* {@link RequestContext.setHeader}. Set cookies from a `'use server'` action, or with Hono's
|
|
289
|
+
* `setCookie(c, …)` in middleware and endpoint routes.
|
|
338
290
|
*
|
|
339
291
|
* @throws If called while a page is rendering, where it could not reach the browser reliably.
|
|
340
292
|
*
|
|
@@ -355,7 +307,6 @@ export class RequestContext {
|
|
|
355
307
|
deleteCookie(this.#raw, name, options);
|
|
356
308
|
},
|
|
357
309
|
};
|
|
358
|
-
/** Guards every write that has to reach the response head. See {@link tooLateToWrite}. */
|
|
359
310
|
#assertWritable(call) {
|
|
360
311
|
if (rendering.has(this.#raw))
|
|
361
312
|
tooLateToWrite(call);
|
|
@@ -364,14 +315,12 @@ export class RequestContext {
|
|
|
364
315
|
* Sets a header on the response — from a `'use server'` action, which is the one place a request
|
|
365
316
|
* context exists *and* the response is still open.
|
|
366
317
|
*
|
|
367
|
-
* From inside a page it throws: a page streams, so
|
|
368
|
-
*
|
|
369
|
-
* a soft navigation — so this refuses rather than doing it half the time.
|
|
318
|
+
* From inside a page it throws: a page streams, so its response head is already committed by then,
|
|
319
|
+
* and the write would land on a full page load but vanish on a soft navigation.
|
|
370
320
|
*
|
|
371
|
-
* Middleware and `{ type: 'endpoint' }` routes
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
* before the render.
|
|
321
|
+
* Middleware and `{ type: 'endpoint' }` routes are handed Hono's `c` directly and use `c.header(…)`.
|
|
322
|
+
* That is also where a header belonging to the *page* goes — `Cache-Control`, `X-Robots-Tag` — since
|
|
323
|
+
* middleware runs before the render.
|
|
375
324
|
*
|
|
376
325
|
* @param name - Header name, case-insensitive.
|
|
377
326
|
* @param value - Header value.
|
|
@@ -393,52 +342,49 @@ export class RequestContext {
|
|
|
393
342
|
this.#assertWritable('ctx.setHeader()');
|
|
394
343
|
this.#raw.header(name, value, options);
|
|
395
344
|
}
|
|
396
|
-
// Hono's response builders, restated as errors naming what to use instead
|
|
397
|
-
//
|
|
398
|
-
// `ctx.
|
|
399
|
-
// autocomplete — and the thrown message is the one that explains. Each takes `...args: unknown[]`
|
|
400
|
-
// it never reads so that `ctx.redirect('/dashboard')` reaches that message rather than stopping at
|
|
401
|
-
// "Expected 0 arguments, but got 1".
|
|
345
|
+
// Hono's response builders, restated as errors naming what to use instead — through `ctx.hono` every
|
|
346
|
+
// one of them is a silent no-op from a page. `@deprecated` strikes them through in autocomplete; the
|
|
347
|
+
// unread `..._args` is so `ctx.redirect('/x')` reaches the thrown message rather than an arity error.
|
|
402
348
|
/** @deprecated Not available on a page's context — use `redirect()` from `@rshono/core/server`. */
|
|
403
|
-
redirect(...
|
|
349
|
+
redirect(..._args) {
|
|
404
350
|
return notOnContext('redirect(location, status?)', "Use `redirect()` from '@rshono/core/server', which throws a signal the framework turns into a real redirect.");
|
|
405
351
|
}
|
|
406
352
|
/** @deprecated Not available on a page's context — use `notFound()` from `@rshono/core/server`. */
|
|
407
|
-
notFound(...
|
|
353
|
+
notFound(..._args) {
|
|
408
354
|
return notOnContext('notFound()', "Use `notFound()` from '@rshono/core/server', which aborts the render and shows the app's not-found page.");
|
|
409
355
|
}
|
|
410
356
|
/** @deprecated A page renders JSX. For a JSON response, use an `{ type: 'endpoint' }` route. */
|
|
411
|
-
json(...
|
|
357
|
+
json(..._args) {
|
|
412
358
|
return notOnContext('json(object)', "For a JSON response use an { type: 'endpoint' } route; to read the request body use `ctx.req.json()`.");
|
|
413
359
|
}
|
|
414
360
|
/** @deprecated A page renders JSX. For a text response, use an `{ type: 'endpoint' }` route. */
|
|
415
|
-
text(...
|
|
361
|
+
text(..._args) {
|
|
416
362
|
return notOnContext('text(string)', "For a text response use an { type: 'endpoint' } route; to read the request body use `ctx.req.text()`.");
|
|
417
363
|
}
|
|
418
364
|
/** @deprecated A page renders JSX, which the framework turns into HTML for you. */
|
|
419
|
-
html(...
|
|
365
|
+
html(..._args) {
|
|
420
366
|
return notOnContext('html(string)', "A page's JSX is already its HTML; for a hand-built HTML response use an { type: 'endpoint' } route.");
|
|
421
367
|
}
|
|
422
368
|
/** @deprecated Not available on a page's context. To read the request body, use `ctx.req`. */
|
|
423
|
-
body(...
|
|
369
|
+
body(..._args) {
|
|
424
370
|
return notOnContext('body(data, …)', "To read the *request* body use `ctx.req.json()` / `ctx.req.text()` / `ctx.req.formData()`; to build a response, use an { type: 'endpoint' } route.");
|
|
425
371
|
}
|
|
426
372
|
/** @deprecated A page's status is set by the framework — use `notFound()`, or an endpoint route. */
|
|
427
|
-
status(...
|
|
373
|
+
status(..._args) {
|
|
428
374
|
return notOnContext('status(code)', "A page's status is the framework's: 200, 404 via `notFound()`, 500 when it throws. For any other code use an { type: 'endpoint' } route.");
|
|
429
375
|
}
|
|
430
376
|
/** @deprecated Renamed — use `ctx.setHeader(name, value)`, which is valid from a `'use server'` action. */
|
|
431
|
-
header(...
|
|
377
|
+
header(..._args) {
|
|
432
378
|
return notOnContext('header(name, value)', "Use `ctx.setHeader(name, value)` from a 'use server' action, or `c.header(…)` in middleware — a page renders too late to set one.");
|
|
433
379
|
}
|
|
434
380
|
}
|
|
435
381
|
/**
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
382
|
+
* The {@link RequestContext} for the current request — URL, cookies, params, env and middleware
|
|
383
|
+
* variables — read from a server component or a server action. Memoised per request, so repeated calls
|
|
384
|
+
* return the same instance.
|
|
439
385
|
*
|
|
440
|
-
* A
|
|
441
|
-
*
|
|
386
|
+
* A page is handed that same object as its `ctx` prop, so this import is for everywhere else: a nested
|
|
387
|
+
* server component, or a `'use server'` action module.
|
|
442
388
|
*
|
|
443
389
|
* @typeParam E - The app's Hono {@link Env}, to type {@link RequestContext.var} and {@link RequestContext.env}.
|
|
444
390
|
* @throws If called at module load, where there is no ambient context to resolve.
|
|
@@ -477,16 +423,15 @@ export function getRequestContext() {
|
|
|
477
423
|
return ctx;
|
|
478
424
|
}
|
|
479
425
|
/**
|
|
480
|
-
* Redirects the request to `location
|
|
481
|
-
*
|
|
426
|
+
* Redirects the request to `location`, by throwing a control signal the framework turns into an HTTP
|
|
427
|
+
* redirect.
|
|
482
428
|
*
|
|
483
|
-
* Because it throws
|
|
484
|
-
*
|
|
485
|
-
* that swallows the signal.
|
|
429
|
+
* Because it throws it never returns, so TypeScript narrows away everything after the call and there
|
|
430
|
+
* is nothing to `return`. Don't wrap it in a `try/catch` that swallows the signal.
|
|
486
431
|
*
|
|
487
432
|
* @param location - Absolute path or URL to redirect to, e.g. `/dashboard`.
|
|
488
|
-
* @param status - Redirect {@link RedirectStatus}; defaults to `303` (See Other),
|
|
489
|
-
* the
|
|
433
|
+
* @param status - Redirect {@link RedirectStatus}; defaults to `303` (See Other), which is what makes
|
|
434
|
+
* the browser follow up with a `GET` after a form action.
|
|
490
435
|
*
|
|
491
436
|
* @example
|
|
492
437
|
* ```ts
|
|
@@ -499,10 +444,10 @@ export function redirect(location, status = 303) {
|
|
|
499
444
|
throw new RedirectSignal(location, status);
|
|
500
445
|
}
|
|
501
446
|
/**
|
|
502
|
-
* Aborts the current render with a 404, rendering the app's
|
|
447
|
+
* Aborts the current render with a 404, rendering the app's `notFound` page.
|
|
503
448
|
*
|
|
504
|
-
* Like {@link redirect}
|
|
505
|
-
*
|
|
449
|
+
* Like {@link redirect} it throws a control signal and never returns, so TypeScript narrows away
|
|
450
|
+
* everything after the call. Don't catch-and-swallow it.
|
|
506
451
|
*
|
|
507
452
|
* @example
|
|
508
453
|
* ```tsx
|
|
@@ -521,12 +466,9 @@ let errorHandler;
|
|
|
521
466
|
* Registers a handler for every error the framework catches, so they can reach an error tracker
|
|
522
467
|
* (Sentry, Datadog, a log pipeline) instead of only `stderr`.
|
|
523
468
|
*
|
|
524
|
-
* Call it
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
* Errors are still written to `stderr` either way, so a handler adds a destination rather than
|
|
528
|
-
* replacing one. A handler that throws is caught and logged: reporting must never be able to fail
|
|
529
|
-
* a request.
|
|
469
|
+
* Call it once, at the top level of `src/server.ts`, which is imported as the server starts.
|
|
470
|
+
* Registering again replaces the previous handler. Errors still go to `stderr` either way, and a
|
|
471
|
+
* handler that throws is caught and logged — reporting can never fail a request.
|
|
530
472
|
*
|
|
531
473
|
* @example
|
|
532
474
|
* ```ts
|
|
@@ -545,10 +487,8 @@ export function onServerError(handler) {
|
|
|
545
487
|
errorHandler = handler;
|
|
546
488
|
}
|
|
547
489
|
/**
|
|
548
|
-
* Logs an error and forwards it to the registered {@link ServerErrorHandler}
|
|
549
|
-
*
|
|
550
|
-
* Framework internal — the single funnel every caught server-side error goes through, so that
|
|
551
|
-
* adding a reporting destination is one registration rather than a hook per call site.
|
|
490
|
+
* Logs an error and forwards it to the registered {@link ServerErrorHandler} — the single funnel every
|
|
491
|
+
* caught server-side error goes through.
|
|
552
492
|
*
|
|
553
493
|
* @internal
|
|
554
494
|
*/
|