@rshono/core 1.0.0-rc.2 → 1.0.0-rc.4
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 +45 -29
- 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/client.d.ts +5 -6
- package/dist/runtime/client.d.ts.map +1 -1
- package/dist/runtime/client.js +5 -6
- 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 +13 -16
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/runtime/navigation.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/runtime/navigation.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAkB,MAAM,OAAO,CAAC;AAqC3E,MAAM,IAAI,GAAG,GAAG,EAAE,GAAE,CAAC,CAAC;AAEtB,MAAM,aAAa,GAAqB,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAErG;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAmB,aAAa,CAAC,CAAC;AAE5E,MAAM,iBAAiB,GAAG,aAAa,CAAyB,IAAI,CAAC,CAAC;AAEtE;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAyE;IAC9H,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,OAAO,CAAkB,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAE/G,OAAO,KAAC,iBAAiB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAA8B,CAAC;AAC3F,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,KAAK,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAC5C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,mKAAmK,CACpK,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["'use client';\n\nimport { createContext, useContext, useMemo, type ReactNode } from 'react';\n\n/**\n * Imperative navigation actions, reached as `useNavigation().router`.\n *\n * `push` / `replace` / `refresh` are **soft** navigations: the new page's flight\n * payload is fetched and applied in place, so client component state outside the\n * changed subtree survives. Off-site or non-HTTP hrefs fall back to a full load.\n */\nexport interface NavigationRouter {\n /** Navigates to `href` and pushes a new history entry. */\n push(href: string): void;\n /** Navigates to `href`, replacing the current history entry instead of adding one. */\n replace(href: string): void;\n /** Re-fetches the current route from the server, re-running its server components. */\n refresh(): void;\n /** `true` while a soft navigation is in flight — use it to disable controls or show a spinner. */\n pending: boolean;\n}\n\n// No `back()` / `forward()`: they were verbatim aliases for `history.back()` and `history.forward()`,\n// which need no framework. Traversal is picked up by the `popstate` listener either way.\n\n/** The current location plus the {@link NavigationRouter}, as returned by {@link useNavigation}. */\nexport interface NavigationState {\n /**\n * The full current {@link URL} — read `url.pathname`, `url.searchParams` and the\n * rest off it. A fresh instance per navigation, so mutating it affects nothing\n * else; it is not written back to the address bar either.\n */\n url: URL;\n /** Matched route params for the current page, e.g. `{ id: '42' }` for `/profile/:id`. */\n params: Record<string, string>;\n /** Imperative navigation actions and the `pending` flag. */\n router: NavigationRouter;\n}\n\nconst noop = () => {};\n\nconst defaultRouter: NavigationRouter = { push: noop, replace: noop, refresh: noop, pending: false };\n\n/**\n * Carries the live {@link NavigationRouter} implementation from the hydration runtime down\n * to {@link RouterProvider}. Framework internal — read the router through\n * {@link useNavigation} instead.\n *\n * @internal\n */\nexport const RouterContext = createContext<NavigationRouter>(defaultRouter);\n\nconst NavigationContext = createContext<NavigationState | null>(null);\n\n/**\n * Publishes the per-render location and params so {@link useNavigation} can read\n * them. Framework internal — the RSC entry wraps every page in one.\n *\n * @internal\n */\nexport function RouterProvider({ href, params, children }: { href: string; params: Record<string, string>; children: ReactNode }) {\n const router = useContext(RouterContext);\n const value = useMemo<NavigationState>(() => ({ url: new URL(href), params, router }), [href, params, router]);\n\n return <NavigationContext.Provider value={value}>{children}</NavigationContext.Provider>;\n}\n\n/**\n * Reactive access to the current URL and programmatic navigation, in one hook.\n *\n * Call it from a `'use client'` component. The location fields (`url` and\n * `params`) are computed on the server and travel in the flight payload, so they\n * are correct during SSR — no hydration flicker — and update automatically on\n * every navigation. The `router` sub-object holds the imperative actions plus a\n * `pending` flag that is `true` while a client navigation is in flight.\n *\n * Hooks can't run in a server component; read the same URL data there from\n * `getRequestContext()` (`@rshono/core/server`) instead.\n *\n * @example\n * ```tsx\n * 'use client';\n * import { useNavigation } from '@rshono/core/client';\n *\n * export function NextPage() {\n * const { url, router } = useNavigation();\n * const page = Number(url.searchParams.get('page') ?? '1');\n * return (\n * <button disabled={router.pending} onClick={() => router.push(`${url.pathname}?page=${page + 1}`)}>\n * Next {router.pending ? '…' : ''}\n * </button>\n * );\n * }\n * ```\n *\n * @returns The current {@link NavigationState}: `url` and `params`, plus `router`\n * ({@link NavigationRouter}) with `push` / `replace` / `refresh` / `pending`.\n * @throws If called outside a page's React tree, where there is no navigation\n * context to read.\n */\nexport function useNavigation(): NavigationState {\n const value = useContext(NavigationContext);\n if (!value) {\n throw new Error(\n \"[rshono] useNavigation() must be called inside a 'use client' component rendered by a page. In a server component, read the URL from getRequestContext() instead.\",\n );\n }\n return value;\n}\n"]}
|
|
@@ -28,6 +28,12 @@ export declare function parseRenderRequest(request: Request): RenderRequest;
|
|
|
28
28
|
export declare function acceptsRsc(request: Request): boolean;
|
|
29
29
|
/** True when the response should be a flight payload rather than an HTML document. */
|
|
30
30
|
export declare function wantsRsc(renderRequest: RenderRequest): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* {@link wantsRsc} straight off the request, for the error and control-signal paths that have no parsed
|
|
33
|
+
* {@link RenderRequest} to hand — they are reached from Hono's `onError` / `notFound`, not from the
|
|
34
|
+
* render, so each one used to re-parse with `wantsRsc(parseRenderRequest(c.req.raw))`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function requestWantsRsc(request: Request): boolean;
|
|
31
37
|
/** True when the request carries a server action to run before rendering. */
|
|
32
38
|
export declare function isActionRequest(renderRequest: RenderRequest): renderRequest is Extract<RenderRequest, {
|
|
33
39
|
kind: 'form-action' | 'rsc-action';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/runtime/request.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAExI,uHAAuH;AACvH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAAG,OAAO,CASpG;AAID,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,aAAa,CAQlE;AAED,gHAAgH;AAChH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEpD;AAED,sFAAsF;AACtF,wBAAgB,QAAQ,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAE9D;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,aAAa,EAAE,aAAa,GAAG,aAAa,IAAI,OAAO,CAAC,aAAa,EAAE;IAAE,IAAI,EAAE,aAAa,GAAG,YAAY,CAAA;CAAE,CAAC,CAE7I"}
|
|
1
|
+
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/runtime/request.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAExI,uHAAuH;AACvH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAAG,OAAO,CASpG;AAID,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,aAAa,CAQlE;AAED,gHAAgH;AAChH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEpD;AAED,sFAAsF;AACtF,wBAAgB,QAAQ,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAE9D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEzD;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,aAAa,EAAE,aAAa,GAAG,aAAa,IAAI,OAAO,CAAC,aAAa,EAAE;IAAE,IAAI,EAAE,aAAa,GAAG,YAAY,CAAA;CAAE,CAAC,CAE7I"}
|
package/dist/runtime/request.js
CHANGED
|
@@ -32,6 +32,14 @@ export function acceptsRsc(request) {
|
|
|
32
32
|
export function wantsRsc(renderRequest) {
|
|
33
33
|
return renderRequest.kind === 'rsc' || renderRequest.kind === 'rsc-action';
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* {@link wantsRsc} straight off the request, for the error and control-signal paths that have no parsed
|
|
37
|
+
* {@link RenderRequest} to hand — they are reached from Hono's `onError` / `notFound`, not from the
|
|
38
|
+
* render, so each one used to re-parse with `wantsRsc(parseRenderRequest(c.req.raw))`.
|
|
39
|
+
*/
|
|
40
|
+
export function requestWantsRsc(request) {
|
|
41
|
+
return wantsRsc(parseRenderRequest(request));
|
|
42
|
+
}
|
|
35
43
|
/** True when the request carries a server action to run before rendering. */
|
|
36
44
|
export function isActionRequest(renderRequest) {
|
|
37
45
|
return renderRequest.kind === 'form-action' || renderRequest.kind === 'rsc-action';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/runtime/request.ts"],"names":[],"mappings":"AAAA,MAAM,gBAAgB,GAAG,cAAc,CAAC;AACxC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAc5C,uHAAuH;AACvH,MAAM,UAAU,gBAAgB,CAAC,SAAiB,EAAE,MAAuC;IACzF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAC1D,IAAI,MAAM;QAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACrD,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE;QACtB,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK;QAC/B,OAAO;QACP,IAAI,EAAE,MAAM,EAAE,IAAI;KACnB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,kBAAkB,GAAG,+DAA+D,CAAC;AAE3F,MAAM,UAAU,kBAAkB,CAAC,OAAgB;IACjD,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACvD,IAAI,QAAQ;YAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC;QACtD,IAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;QACvG,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC9B,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;AAC5D,CAAC;AAED,gHAAgH;AAChH,MAAM,UAAU,UAAU,CAAC,OAAgB;IACzC,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,gBAAgB,CAAC,IAAI,KAAK,CAAC;AAC5E,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,QAAQ,CAAC,aAA4B;IACnD,OAAO,aAAa,CAAC,IAAI,KAAK,KAAK,IAAI,aAAa,CAAC,IAAI,KAAK,YAAY,CAAC;AAC7E,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,eAAe,CAAC,aAA4B;IAC1D,OAAO,aAAa,CAAC,IAAI,KAAK,aAAa,IAAI,aAAa,CAAC,IAAI,KAAK,YAAY,CAAC;AACrF,CAAC","sourcesContent":["const HEADER_ACTION_ID = 'x-rsc-action';\nconst RSC_CONTENT_TYPE = 'text/x-component';\n\n/**\n * The four distinct shapes an incoming render request can take, modeled as a\n * discriminated union so illegal combinations (e.g. an action id on a GET) are\n * unrepresentable and every consumer dispatches exhaustively on `kind`.\n *\n * - `document` — a normal navigation; respond with a full SSR HTML document.\n * - `rsc` — a soft-navigation flight fetch (`Accept: text/x-component`).\n * - `form-action` — a progressive-enhancement `<form>` POST (no JavaScript).\n * - `rsc-action` — a client-initiated server-action call carrying an action id.\n */\nexport type RenderRequest = { kind: 'document' } | { kind: 'rsc' } | { kind: 'form-action' } | { kind: 'rsc-action'; actionId: string };\n\n/** Builds the `Request` the client sends to ask a page for its flight payload, optionally carrying a server action. */\nexport function createRscRequest(urlString: string, action?: { id: string; body: BodyInit }): Request {\n const url = new URL(urlString, location.origin);\n const headers = new Headers({ Accept: RSC_CONTENT_TYPE });\n if (action) headers.set(HEADER_ACTION_ID, action.id);\n return new Request(url, {\n method: action ? 'POST' : 'GET',\n headers,\n body: action?.body,\n });\n}\n\nconst FORM_CONTENT_TYPES = /^(?:multipart\\/form-data|application\\/x-www-form-urlencoded)/i;\n\nexport function parseRenderRequest(request: Request): RenderRequest {\n if (request.method === 'POST') {\n const actionId = request.headers.get(HEADER_ACTION_ID);\n if (actionId) return { kind: 'rsc-action', actionId };\n if (FORM_CONTENT_TYPES.test(request.headers.get('content-type') ?? '')) return { kind: 'form-action' };\n return { kind: 'document' };\n }\n return { kind: acceptsRsc(request) ? 'rsc' : 'document' };\n}\n\n/** Whether the client asked for a flight payload. For GET paths that only need the boolean, without parsing. */\nexport function acceptsRsc(request: Request): boolean {\n return request.headers.get('accept')?.includes(RSC_CONTENT_TYPE) ?? false;\n}\n\n/** True when the response should be a flight payload rather than an HTML document. */\nexport function wantsRsc(renderRequest: RenderRequest): boolean {\n return renderRequest.kind === 'rsc' || renderRequest.kind === 'rsc-action';\n}\n\n/** True when the request carries a server action to run before rendering. */\nexport function isActionRequest(renderRequest: RenderRequest): renderRequest is Extract<RenderRequest, { kind: 'form-action' | 'rsc-action' }> {\n return renderRequest.kind === 'form-action' || renderRequest.kind === 'rsc-action';\n}\n"]}
|
|
1
|
+
{"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/runtime/request.ts"],"names":[],"mappings":"AAAA,MAAM,gBAAgB,GAAG,cAAc,CAAC;AACxC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAc5C,uHAAuH;AACvH,MAAM,UAAU,gBAAgB,CAAC,SAAiB,EAAE,MAAuC;IACzF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAC1D,IAAI,MAAM;QAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACrD,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE;QACtB,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK;QAC/B,OAAO;QACP,IAAI,EAAE,MAAM,EAAE,IAAI;KACnB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,kBAAkB,GAAG,+DAA+D,CAAC;AAE3F,MAAM,UAAU,kBAAkB,CAAC,OAAgB;IACjD,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACvD,IAAI,QAAQ;YAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC;QACtD,IAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;QACvG,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC9B,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;AAC5D,CAAC;AAED,gHAAgH;AAChH,MAAM,UAAU,UAAU,CAAC,OAAgB;IACzC,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,gBAAgB,CAAC,IAAI,KAAK,CAAC;AAC5E,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,QAAQ,CAAC,aAA4B;IACnD,OAAO,aAAa,CAAC,IAAI,KAAK,KAAK,IAAI,aAAa,CAAC,IAAI,KAAK,YAAY,CAAC;AAC7E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,OAAO,QAAQ,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,eAAe,CAAC,aAA4B;IAC1D,OAAO,aAAa,CAAC,IAAI,KAAK,aAAa,IAAI,aAAa,CAAC,IAAI,KAAK,YAAY,CAAC;AACrF,CAAC","sourcesContent":["const HEADER_ACTION_ID = 'x-rsc-action';\nconst RSC_CONTENT_TYPE = 'text/x-component';\n\n/**\n * The four distinct shapes an incoming render request can take, modeled as a\n * discriminated union so illegal combinations (e.g. an action id on a GET) are\n * unrepresentable and every consumer dispatches exhaustively on `kind`.\n *\n * - `document` — a normal navigation; respond with a full SSR HTML document.\n * - `rsc` — a soft-navigation flight fetch (`Accept: text/x-component`).\n * - `form-action` — a progressive-enhancement `<form>` POST (no JavaScript).\n * - `rsc-action` — a client-initiated server-action call carrying an action id.\n */\nexport type RenderRequest = { kind: 'document' } | { kind: 'rsc' } | { kind: 'form-action' } | { kind: 'rsc-action'; actionId: string };\n\n/** Builds the `Request` the client sends to ask a page for its flight payload, optionally carrying a server action. */\nexport function createRscRequest(urlString: string, action?: { id: string; body: BodyInit }): Request {\n const url = new URL(urlString, location.origin);\n const headers = new Headers({ Accept: RSC_CONTENT_TYPE });\n if (action) headers.set(HEADER_ACTION_ID, action.id);\n return new Request(url, {\n method: action ? 'POST' : 'GET',\n headers,\n body: action?.body,\n });\n}\n\nconst FORM_CONTENT_TYPES = /^(?:multipart\\/form-data|application\\/x-www-form-urlencoded)/i;\n\nexport function parseRenderRequest(request: Request): RenderRequest {\n if (request.method === 'POST') {\n const actionId = request.headers.get(HEADER_ACTION_ID);\n if (actionId) return { kind: 'rsc-action', actionId };\n if (FORM_CONTENT_TYPES.test(request.headers.get('content-type') ?? '')) return { kind: 'form-action' };\n return { kind: 'document' };\n }\n return { kind: acceptsRsc(request) ? 'rsc' : 'document' };\n}\n\n/** Whether the client asked for a flight payload. For GET paths that only need the boolean, without parsing. */\nexport function acceptsRsc(request: Request): boolean {\n return request.headers.get('accept')?.includes(RSC_CONTENT_TYPE) ?? false;\n}\n\n/** True when the response should be a flight payload rather than an HTML document. */\nexport function wantsRsc(renderRequest: RenderRequest): boolean {\n return renderRequest.kind === 'rsc' || renderRequest.kind === 'rsc-action';\n}\n\n/**\n * {@link wantsRsc} straight off the request, for the error and control-signal paths that have no parsed\n * {@link RenderRequest} to hand — they are reached from Hono's `onError` / `notFound`, not from the\n * render, so each one used to re-parse with `wantsRsc(parseRenderRequest(c.req.raw))`.\n */\nexport function requestWantsRsc(request: Request): boolean {\n return wantsRsc(parseRenderRequest(request));\n}\n\n/** True when the request carries a server action to run before rendering. */\nexport function isActionRequest(renderRequest: RenderRequest): renderRequest is Extract<RenderRequest, { kind: 'form-action' | 'rsc-action' }> {\n return renderRequest.kind === 'form-action' || renderRequest.kind === 'rsc-action';\n}\n"]}
|
package/dist/runtime/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@rshono/core/server` — the request-scoped surface, for use inside server components
|
|
3
|
-
* and `'use server'` action modules: {@link
|
|
3
|
+
* and `'use server'` action modules: {@link getRequestContext} for the URL, cookies,
|
|
4
4
|
* params, env and middleware variables, the {@link redirect} and {@link notFound}
|
|
5
5
|
* control-flow helpers, and {@link onServerError} for reporting the errors the
|
|
6
6
|
* framework catches.
|
|
@@ -20,5 +20,5 @@
|
|
|
20
20
|
*
|
|
21
21
|
* @packageDocumentation
|
|
22
22
|
*/
|
|
23
|
-
export {
|
|
23
|
+
export { getRequestContext, notFound, onServerError, redirect, type EnvVars, type RedirectStatus, type RequestContext, type ServerErrorContext, type ServerErrorHandler, type ServerErrorSource, } from './context.js';
|
|
24
24
|
//# sourceMappingURL=server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/runtime/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/runtime/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EACL,iBAAiB,EACjB,QAAQ,EACR,aAAa,EACb,QAAQ,EACR,KAAK,OAAO,EACZ,KAAK,cAAc,EAGnB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,cAAc,CAAC"}
|
package/dist/runtime/server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@rshono/core/server` — the request-scoped surface, for use inside server components
|
|
3
|
-
* and `'use server'` action modules: {@link
|
|
3
|
+
* and `'use server'` action modules: {@link getRequestContext} for the URL, cookies,
|
|
4
4
|
* params, env and middleware variables, the {@link redirect} and {@link notFound}
|
|
5
5
|
* control-flow helpers, and {@link onServerError} for reporting the errors the
|
|
6
6
|
* framework catches.
|
|
@@ -20,5 +20,5 @@
|
|
|
20
20
|
*
|
|
21
21
|
* @packageDocumentation
|
|
22
22
|
*/
|
|
23
|
-
export {
|
|
23
|
+
export { getRequestContext, notFound, onServerError, redirect, } from './context.js';
|
|
24
24
|
//# sourceMappingURL=server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/runtime/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/runtime/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EACL,iBAAiB,EACjB,QAAQ,EACR,aAAa,EACb,QAAQ,GAST,MAAM,cAAc,CAAC","sourcesContent":["/**\n * `@rshono/core/server` — the request-scoped surface, for use inside server components\n * and `'use server'` action modules: {@link getRequestContext} for the URL, cookies,\n * params, env and middleware variables, the {@link redirect} and {@link notFound}\n * control-flow helpers, and {@link onServerError} for reporting the errors the\n * framework catches.\n *\n * Server-only. Importing this from a `'use client'` module is a mistake — those\n * run in the browser (and are SSR'd without a bound context). Read what you need\n * on the server and pass it down as props, or use `useNavigation()` from\n * `@rshono/core/client` for URL data.\n *\n * A barrel rather than `./context.js` itself, which is where all of this is\n * implemented: that module also exports the plumbing the framework's own entry\n * points need (`runWithContext`, `readParams`, `publicUrl`, `reportServerError`),\n * and pointing the `./server` subpath straight at it published every one of them —\n * `@internal` in the docs, but present in a consumer's autocomplete and pinned by\n * semver all the same. They stay reachable by relative import, which is the only\n * way the framework itself ever reaches them.\n *\n * @packageDocumentation\n */\n\nexport {\n getRequestContext,\n notFound,\n onServerError,\n redirect,\n type EnvVars,\n type RedirectStatus,\n // A type, not a value: `RequestContext` is handed to you by `getRequestContext()` or the `ctx` page\n // prop — one instance per request — and is never constructed by application code.\n type RequestContext,\n type ServerErrorContext,\n type ServerErrorHandler,\n type ServerErrorSource,\n} from './context.js';\n"]}
|
package/dist/server/headers.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Small header utilities shared by the response-header defaults
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Small header utilities shared by the response-header defaults and the prerendered-page cache. Kept
|
|
3
|
+
* apart from both because getting `Vary` and `ETag` comparison subtly wrong is exactly the kind of
|
|
4
|
+
* thing that only shows up behind a CDN.
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
7
|
* Adds `value` to the `Vary` header without discarding what is already there.
|
|
8
8
|
*
|
|
9
|
-
* `Vary` is a list, and
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* `Vary` is a list, and the framework's own entry (`Accept` — an HTML document or a flight payload from
|
|
10
|
+
* one URL) is not necessarily the only one: a route, a middleware or a proxy may have added its own.
|
|
11
|
+
* A plain `set` would drop the others, and a cache would then serve one variant in place of another.
|
|
12
|
+
* `*` is left alone: it already means "never reuse this".
|
|
13
13
|
*/
|
|
14
14
|
export declare function appendVary(headers: Headers, value: string): void;
|
|
15
15
|
/**
|
|
@@ -17,8 +17,8 @@ export declare function appendVary(headers: Headers, value: string): void;
|
|
|
17
17
|
* exact body and should be answered with a 304.
|
|
18
18
|
*
|
|
19
19
|
* The header carries a *list*, and each entry may be weak (`W/"…"`). The weak prefix is ignored on
|
|
20
|
-
* both sides
|
|
21
|
-
*
|
|
20
|
+
* both sides: a proxy or CDN that gzips on the way out changes the bytes without changing the
|
|
21
|
+
* representation, and is entitled to weaken the validator when it does.
|
|
22
22
|
*/
|
|
23
23
|
export declare function etagMatches(ifNoneMatch: string | undefined, etag: string): boolean;
|
|
24
24
|
//# sourceMappingURL=headers.d.ts.map
|
package/dist/server/headers.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Small header utilities shared by the response-header defaults
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Small header utilities shared by the response-header defaults and the prerendered-page cache. Kept
|
|
3
|
+
* apart from both because getting `Vary` and `ETag` comparison subtly wrong is exactly the kind of
|
|
4
|
+
* thing that only shows up behind a CDN.
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
7
|
* Adds `value` to the `Vary` header without discarding what is already there.
|
|
8
8
|
*
|
|
9
|
-
* `Vary` is a list, and
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* `Vary` is a list, and the framework's own entry (`Accept` — an HTML document or a flight payload from
|
|
10
|
+
* one URL) is not necessarily the only one: a route, a middleware or a proxy may have added its own.
|
|
11
|
+
* A plain `set` would drop the others, and a cache would then serve one variant in place of another.
|
|
12
|
+
* `*` is left alone: it already means "never reuse this".
|
|
13
13
|
*/
|
|
14
14
|
export function appendVary(headers, value) {
|
|
15
15
|
const existing = headers.get('vary');
|
|
@@ -28,8 +28,8 @@ export function appendVary(headers, value) {
|
|
|
28
28
|
* exact body and should be answered with a 304.
|
|
29
29
|
*
|
|
30
30
|
* The header carries a *list*, and each entry may be weak (`W/"…"`). The weak prefix is ignored on
|
|
31
|
-
* both sides
|
|
32
|
-
*
|
|
31
|
+
* both sides: a proxy or CDN that gzips on the way out changes the bytes without changing the
|
|
32
|
+
* representation, and is entitled to weaken the validator when it does.
|
|
33
33
|
*/
|
|
34
34
|
export function etagMatches(ifNoneMatch, etag) {
|
|
35
35
|
if (!ifNoneMatch)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headers.js","sourceRoot":"","sources":["../../src/server/headers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,OAAgB,EAAE,KAAa;IACxD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG;QAAE,OAAO;IACpC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;IACxG,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,WAA+B,EAAE,IAAY;IACvE,IAAI,CAAC,WAAW;QAAE,OAAO,KAAK,CAAC;IAC/B,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;QAC3C,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QACnC,OAAO,SAAS,KAAK,GAAG,IAAI,SAAS,KAAK,MAAM,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * Small header utilities shared by the response-header defaults
|
|
1
|
+
{"version":3,"file":"headers.js","sourceRoot":"","sources":["../../src/server/headers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,OAAgB,EAAE,KAAa;IACxD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG;QAAE,OAAO;IACpC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;IACxG,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,WAA+B,EAAE,IAAY;IACvE,IAAI,CAAC,WAAW;QAAE,OAAO,KAAK,CAAC;IAC/B,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;QAC3C,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QACnC,OAAO,SAAS,KAAK,GAAG,IAAI,SAAS,KAAK,MAAM,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * Small header utilities shared by the response-header defaults and the prerendered-page cache. Kept\n * apart from both because getting `Vary` and `ETag` comparison subtly wrong is exactly the kind of\n * thing that only shows up behind a CDN.\n */\n\n/**\n * Adds `value` to the `Vary` header without discarding what is already there.\n *\n * `Vary` is a list, and the framework's own entry (`Accept` — an HTML document or a flight payload from\n * one URL) is not necessarily the only one: a route, a middleware or a proxy may have added its own.\n * A plain `set` would drop the others, and a cache would then serve one variant in place of another.\n * `*` is left alone: it already means \"never reuse this\".\n */\nexport function appendVary(headers: Headers, value: string): void {\n const existing = headers.get('vary');\n if (existing === null) {\n headers.set('vary', value);\n return;\n }\n if (existing.trim() === '*') return;\n const already = existing.split(',').some((entry) => entry.trim().toLowerCase() === value.toLowerCase());\n if (!already) headers.set('vary', `${existing}, ${value}`);\n}\n\n/**\n * True when an `If-None-Match` request header matches `etag`, i.e. the client already holds this\n * exact body and should be answered with a 304.\n *\n * The header carries a *list*, and each entry may be weak (`W/\"…\"`). The weak prefix is ignored on\n * both sides: a proxy or CDN that gzips on the way out changes the bytes without changing the\n * representation, and is entitled to weaken the validator when it does.\n */\nexport function etagMatches(ifNoneMatch: string | undefined, etag: string): boolean {\n if (!ifNoneMatch) return false;\n const normalize = (value: string) => value.trim().replace(/^W\\//, '');\n const wanted = normalize(etag);\n return ifNoneMatch.split(',').some((entry) => {\n const candidate = normalize(entry);\n return candidate === '*' || candidate === wanted;\n });\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RshonoConfig } from '../config.js';
|
|
2
2
|
/**
|
|
3
3
|
* Load the project config, or `{}` if none exists. Scans `rshono.config.{ts,js,mjs}` at
|
|
4
4
|
* {@link rootDir} unless an explicit {@link configPath} is given (resolved relative to `cwd`).
|
|
5
5
|
*/
|
|
6
|
-
export declare function loadConfig(rootDir: string, configPath?: string): Promise<
|
|
6
|
+
export declare function loadConfig(rootDir: string, configPath?: string): Promise<RshonoConfig>;
|
|
7
7
|
//# sourceMappingURL=load-config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-config.d.ts","sourceRoot":"","sources":["../../src/server/load-config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAmBjD;;;GAGG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"load-config.d.ts","sourceRoot":"","sources":["../../src/server/load-config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAmBjD;;;GAGG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA6B5F"}
|
|
@@ -5,17 +5,16 @@ const CONFIG_FILES = ['rshono.config.ts', 'rshono.config.js', 'rshono.config.mjs
|
|
|
5
5
|
/**
|
|
6
6
|
* Imports the config module.
|
|
7
7
|
*
|
|
8
|
-
* A `.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* A `.ts` config needs no loader of its own: Node strips types natively from 22.18 on, which is what
|
|
9
|
+
* the package's `engines` floor is for. That replaced a `tsx` dependency whose only job was these two
|
|
10
|
+
* lines — and `tsx` bundles esbuild, so it was the largest thing in the install.
|
|
11
|
+
*
|
|
12
|
+
* The one thing native stripping will not do is resolve a `.js` specifier to a `.ts` file, so a config
|
|
13
|
+
* that imports a sibling module has to name it with its real extension. {@link loadConfig} turns the
|
|
14
|
+
* resulting resolution failure into that advice rather than letting a raw `ERR_MODULE_NOT_FOUND` out.
|
|
12
15
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (!/\.[cm]?ts$/.test(file))
|
|
16
|
-
return import(href);
|
|
17
|
-
const { tsImport } = await import('tsx/esm/api');
|
|
18
|
-
return tsImport(href, import.meta.url);
|
|
16
|
+
function importConfig(file) {
|
|
17
|
+
return import(pathToFileURL(file).href);
|
|
19
18
|
}
|
|
20
19
|
/**
|
|
21
20
|
* Load the project config, or `{}` if none exists. Scans `rshono.config.{ts,js,mjs}` at
|
|
@@ -32,7 +31,19 @@ export async function loadConfig(rootDir, configPath) {
|
|
|
32
31
|
if (!existsSync(file)) {
|
|
33
32
|
throw new Error(`[rshono] config file not found: ${file}`);
|
|
34
33
|
}
|
|
35
|
-
|
|
34
|
+
let mod;
|
|
35
|
+
try {
|
|
36
|
+
mod = await importConfig(file);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
// The one failure mode native type stripping has that a TypeScript-aware loader does not.
|
|
40
|
+
if (/\.[cm]?ts$/.test(file) && error?.code === 'ERR_MODULE_NOT_FOUND') {
|
|
41
|
+
throw new Error(`[rshono] ${file} imports a module Node could not resolve: ${error.message}\n` +
|
|
42
|
+
' A .ts config is loaded by Node itself, which does not rewrite a .js specifier to the .ts file ' +
|
|
43
|
+
'beside it. Import it by its real extension, or move the config to rshono.config.mjs.', { cause: error });
|
|
44
|
+
}
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
36
47
|
if (!mod.default) {
|
|
37
48
|
throw new Error(`[rshono] ${file} must \`export default\` a config object (use \`defineConfig({ … })\`).`);
|
|
38
49
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-config.js","sourceRoot":"","sources":["../../src/server/load-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,YAAY,GAAG,CAAC,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,CAAC;AAEnF
|
|
1
|
+
{"version":3,"file":"load-config.js","sourceRoot":"","sources":["../../src/server/load-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,YAAY,GAAG,CAAC,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,CAAC;AAEnF;;;;;;;;;;GAUG;AACH,SAAS,YAAY,CAAC,IAAY;IAChC,OAAO,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,CAAwC,CAAC;AACjF,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,UAAmB;IACnE,MAAM,IAAI,GAAG,UAAU;QACrB,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;YACtB,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC;QACtC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/D,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,IAAI,GAA+B,CAAC;IACpC,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,0FAA0F;QAC1F,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAK,KAAkC,EAAE,IAAI,KAAK,sBAAsB,EAAE,CAAC;YACpG,MAAM,IAAI,KAAK,CACb,YAAY,IAAI,6CAA8C,KAAe,CAAC,OAAO,IAAI;gBACvF,kGAAkG;gBAClG,sFAAsF,EACxF,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,yEAAyE,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO,GAAG,CAAC,OAAO,CAAC;AACrB,CAAC","sourcesContent":["import { existsSync } from 'node:fs';\nimport { isAbsolute, join, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { RshonoConfig } from '../config.js';\n\nconst CONFIG_FILES = ['rshono.config.ts', 'rshono.config.js', 'rshono.config.mjs'];\n\n/**\n * Imports the config module.\n *\n * A `.ts` config needs no loader of its own: Node strips types natively from 22.18 on, which is what\n * the package's `engines` floor is for. That replaced a `tsx` dependency whose only job was these two\n * lines — and `tsx` bundles esbuild, so it was the largest thing in the install.\n *\n * The one thing native stripping will not do is resolve a `.js` specifier to a `.ts` file, so a config\n * that imports a sibling module has to name it with its real extension. {@link loadConfig} turns the\n * resulting resolution failure into that advice rather than letting a raw `ERR_MODULE_NOT_FOUND` out.\n */\nfunction importConfig(file: string): Promise<{ default?: RshonoConfig }> {\n return import(pathToFileURL(file).href) as Promise<{ default?: RshonoConfig }>;\n}\n\n/**\n * Load the project config, or `{}` if none exists. Scans `rshono.config.{ts,js,mjs}` at\n * {@link rootDir} unless an explicit {@link configPath} is given (resolved relative to `cwd`).\n */\nexport async function loadConfig(rootDir: string, configPath?: string): Promise<RshonoConfig> {\n const file = configPath\n ? isAbsolute(configPath)\n ? configPath\n : resolve(process.cwd(), configPath)\n : CONFIG_FILES.map((f) => join(rootDir, f)).find(existsSync);\n if (!file) return {};\n if (!existsSync(file)) {\n throw new Error(`[rshono] config file not found: ${file}`);\n }\n let mod: { default?: RshonoConfig };\n try {\n mod = await importConfig(file);\n } catch (error) {\n // The one failure mode native type stripping has that a TypeScript-aware loader does not.\n if (/\\.[cm]?ts$/.test(file) && (error as { code?: string } | null)?.code === 'ERR_MODULE_NOT_FOUND') {\n throw new Error(\n `[rshono] ${file} imports a module Node could not resolve: ${(error as Error).message}\\n` +\n ' A .ts config is loaded by Node itself, which does not rewrite a .js specifier to the .ts file ' +\n 'beside it. Import it by its real extension, or move the config to rshono.config.mjs.',\n { cause: error },\n );\n }\n throw error;\n }\n if (!mod.default) {\n throw new Error(`[rshono] ${file} must \\`export default\\` a config object (use \\`defineConfig({ … })\\`).`);\n }\n return mod.default;\n}\n"]}
|
|
@@ -42,25 +42,68 @@ export declare function ssgFilePath(routePath: string, variant?: PrerenderVarian
|
|
|
42
42
|
* fall back on, so `..` has to be refused here or not at all.
|
|
43
43
|
*/
|
|
44
44
|
export declare function prerenderedRelPath(requestPath: string, variant: PrerenderVariant): string | null;
|
|
45
|
+
/**
|
|
46
|
+
* A bounded, insertion-ordered cache of prerendered pages.
|
|
47
|
+
*
|
|
48
|
+
* Bounded so a site with thousands of prerendered pages keeps a working set rather than the whole build
|
|
49
|
+
* in memory. Only *hits* are ever stored: caching misses would let anyone mint entries by requesting
|
|
50
|
+
* paths that don't exist. The files are written at build time and never change while the server is up,
|
|
51
|
+
* so an entry never needs invalidating.
|
|
52
|
+
*
|
|
53
|
+
* Shared because both targets need exactly this and had a hand-rolled copy of it, down to the same
|
|
54
|
+
* eviction line.
|
|
55
|
+
*/
|
|
56
|
+
export declare function createPageCache(max?: number): {
|
|
57
|
+
get(key: string): PrerenderedPage | undefined;
|
|
58
|
+
set(key: string, page: PrerenderedPage): void;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* A weak `ETag` for a page body.
|
|
62
|
+
*
|
|
63
|
+
* Web Crypto rather than `node:crypto`, so the one implementation serves both a Node server and
|
|
64
|
+
* `workerd` — the Cloudflare runtime used to carry its own copy of this, with its own base64url
|
|
65
|
+
* conversion, purely because the other one reached for a Node builtin.
|
|
66
|
+
*
|
|
67
|
+
* Deliberately **weak**: the bytes on the wire depend on whether the client took gzip, and a strong
|
|
68
|
+
* validator would have to differ between those two — so the 200 and the 304 that revalidates it would
|
|
69
|
+
* disagree, and a cache would treat them as different pages. A weak tag says "the same
|
|
70
|
+
* representation", which is exactly what is true across content codings.
|
|
71
|
+
*/
|
|
72
|
+
export declare function weakEtag(body: Uint8Array<ArrayBuffer>): Promise<string>;
|
|
73
|
+
/**
|
|
74
|
+
* Assembles a {@link PrerenderedPage} from a body just read out of the build.
|
|
75
|
+
*
|
|
76
|
+
* `storeEtag` is the validator the store supplied, where it has one — it already describes these exact
|
|
77
|
+
* bytes, so it is preferred over hashing them again, and only weakened. Both call sites went through
|
|
78
|
+
* their own copy of this before, with two different digest implementations.
|
|
79
|
+
*/
|
|
80
|
+
export declare function toPrerenderedPage(body: Uint8Array<ArrayBuffer>, storeEtag?: string | null): Promise<PrerenderedPage>;
|
|
45
81
|
/** A prerendered page, ready to serve: its body and a validator derived from those exact bytes. */
|
|
46
82
|
export interface PrerenderedPage {
|
|
47
|
-
/**
|
|
48
|
-
|
|
83
|
+
/**
|
|
84
|
+
* The document or the flight payload, depending on which {@link PrerenderVariant} was read.
|
|
85
|
+
*
|
|
86
|
+
* Bytes rather than a string, because this is a *cache* entry served verbatim to every request that
|
|
87
|
+
* hits it. Held as a string, each of those requests pays a fresh UTF-8 encode of the whole page on the
|
|
88
|
+
* way out — the one route where the framework does no rendering at all was spending its time
|
|
89
|
+
* re-encoding bytes it had already encoded once to measure them. Read once, encoded never.
|
|
90
|
+
*/
|
|
91
|
+
body: Uint8Array<ArrayBuffer>;
|
|
49
92
|
/**
|
|
50
93
|
* `Content-Length` for {@link body}, in bytes rather than characters.
|
|
51
94
|
*
|
|
52
|
-
* Served with the response because Hono sets no length for an in-memory body, and
|
|
53
|
-
*
|
|
54
|
-
*
|
|
95
|
+
* Served with the response because Hono sets no length for an in-memory body, and a proxy or CDN in
|
|
96
|
+
* front is entitled to make decisions with it — whether gzipping this response is worth the framing,
|
|
97
|
+
* most obviously.
|
|
55
98
|
*/
|
|
56
99
|
contentLength: string;
|
|
57
100
|
/**
|
|
58
101
|
* `ETag` for the page, so a revalidating client can be answered with a 304 instead of the body.
|
|
59
102
|
*
|
|
60
|
-
* Deliberately **weak**. The bytes on the wire depend on whether
|
|
61
|
-
* strong validator would have to differ
|
|
62
|
-
*
|
|
63
|
-
*
|
|
103
|
+
* Deliberately **weak**. The bytes on the wire depend on whether something in front re-encoded them,
|
|
104
|
+
* and a strong validator would have to differ per coding — so the 200 and the 304 that revalidates it
|
|
105
|
+
* could disagree, and a cache would treat them as different pages. A weak tag says "the same
|
|
106
|
+
* representation", which is exactly what stays true across content codings.
|
|
64
107
|
*/
|
|
65
108
|
etag: string;
|
|
66
109
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prerendered.d.ts","sourceRoot":"","sources":["../../src/server/prerendered.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEjD,eAAO,MAAM,QAAQ;;iBACX,IAAI,EAAE,YAAY;iBAAE,MAAM,EAAE,WAAW;iBAAE,WAAW,EAAE,WAAW;;;iBAC/D,IAAI,EAAE,WAAW;iBAAE,MAAM,EAAE,kBAAkB;iBAAE,WAAW,EAAE,kBAAkB;;CACU,CAAC;AAErG;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAyB,GAAG,MAAM,GAAG,IAAI,CAKhG;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAGhG;AAED,mGAAmG;AACnG,MAAM,WAAW,eAAe;IAC9B
|
|
1
|
+
{"version":3,"file":"prerendered.d.ts","sourceRoot":"","sources":["../../src/server/prerendered.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEjD,eAAO,MAAM,QAAQ;;iBACX,IAAI,EAAE,YAAY;iBAAE,MAAM,EAAE,WAAW;iBAAE,WAAW,EAAE,WAAW;;;iBAC/D,IAAI,EAAE,WAAW;iBAAE,MAAM,EAAE,kBAAkB;iBAAE,WAAW,EAAE,kBAAkB;;CACU,CAAC;AAErG;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAyB,GAAG,MAAM,GAAG,IAAI,CAKhG;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAGhG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,GAAG,SAAM,GAAG;IAAE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAC;IAAC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,IAAI,CAAA;CAAE,CAU3I;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAO7E;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,eAAe,CAAC,CAM1H;AAED,mGAAmG;AACnG,MAAM,WAAW,eAAe;IAC9B;;;;;;;OAOG;IACH,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;IAC9B;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;;;OAOG;IACH,IAAI,EAAE,MAAM,CAAC;CACd"}
|
|
@@ -36,4 +36,61 @@ export function prerenderedRelPath(requestPath, variant) {
|
|
|
36
36
|
return null;
|
|
37
37
|
return ssgFilePath(requestPath, variant);
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* A bounded, insertion-ordered cache of prerendered pages.
|
|
41
|
+
*
|
|
42
|
+
* Bounded so a site with thousands of prerendered pages keeps a working set rather than the whole build
|
|
43
|
+
* in memory. Only *hits* are ever stored: caching misses would let anyone mint entries by requesting
|
|
44
|
+
* paths that don't exist. The files are written at build time and never change while the server is up,
|
|
45
|
+
* so an entry never needs invalidating.
|
|
46
|
+
*
|
|
47
|
+
* Shared because both targets need exactly this and had a hand-rolled copy of it, down to the same
|
|
48
|
+
* eviction line.
|
|
49
|
+
*/
|
|
50
|
+
export function createPageCache(max = 128) {
|
|
51
|
+
const pages = new Map();
|
|
52
|
+
return {
|
|
53
|
+
get: (key) => pages.get(key),
|
|
54
|
+
set(key, page) {
|
|
55
|
+
pages.set(key, page);
|
|
56
|
+
// Insertion-ordered, so the first key is the oldest. One entry in means at most one out.
|
|
57
|
+
if (pages.size > max)
|
|
58
|
+
pages.delete(pages.keys().next().value);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* A weak `ETag` for a page body.
|
|
64
|
+
*
|
|
65
|
+
* Web Crypto rather than `node:crypto`, so the one implementation serves both a Node server and
|
|
66
|
+
* `workerd` — the Cloudflare runtime used to carry its own copy of this, with its own base64url
|
|
67
|
+
* conversion, purely because the other one reached for a Node builtin.
|
|
68
|
+
*
|
|
69
|
+
* Deliberately **weak**: the bytes on the wire depend on whether the client took gzip, and a strong
|
|
70
|
+
* validator would have to differ between those two — so the 200 and the 304 that revalidates it would
|
|
71
|
+
* disagree, and a cache would treat them as different pages. A weak tag says "the same
|
|
72
|
+
* representation", which is exactly what is true across content codings.
|
|
73
|
+
*/
|
|
74
|
+
export async function weakEtag(body) {
|
|
75
|
+
const digest = await crypto.subtle.digest('SHA-256', body);
|
|
76
|
+
const base64url = btoa(String.fromCharCode(...new Uint8Array(digest)))
|
|
77
|
+
.replace(/\+/g, '-')
|
|
78
|
+
.replace(/\//g, '_')
|
|
79
|
+
.replace(/=+$/, '');
|
|
80
|
+
return `W/"${base64url.slice(0, 22)}"`;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Assembles a {@link PrerenderedPage} from a body just read out of the build.
|
|
84
|
+
*
|
|
85
|
+
* `storeEtag` is the validator the store supplied, where it has one — it already describes these exact
|
|
86
|
+
* bytes, so it is preferred over hashing them again, and only weakened. Both call sites went through
|
|
87
|
+
* their own copy of this before, with two different digest implementations.
|
|
88
|
+
*/
|
|
89
|
+
export async function toPrerenderedPage(body, storeEtag) {
|
|
90
|
+
return {
|
|
91
|
+
body,
|
|
92
|
+
contentLength: String(body.byteLength),
|
|
93
|
+
etag: storeEtag ? storeEtag.replace(/^(?!W\/)/, 'W/') : await weakEtag(body),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
39
96
|
//# sourceMappingURL=prerendered.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prerendered.js","sourceRoot":"","sources":["../../src/server/prerendered.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,IAAI,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE;IAC3E,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,kBAAkB,EAAE,WAAW,EAAE,kBAAkB,EAAE;CACQ,CAAC;AAErG;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,OAAO,GAAqB,MAAM;IAC/E,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC;IACpC,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,IAAI,EAAE,CAAC;AACtD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,WAAmB,EAAE,OAAyB;IAC/E,IAAI,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC;QAAE,OAAO,IAAI,CAAC;IACvD,OAAO,WAAW,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC","sourcesContent":["/**\n * Everything about a prerendered page that doesn't need a filesystem: where the build puts it, and\n * what it looks like once read back.\n *\n * Split out of `ssg.ts` — which owns the writing and the reading, both through `node:fs` — because a\n * deploy target without a filesystem reads the very same layout out of an asset store, and importing\n * it from the fs module would drag `node:fs` and `node:crypto` into a bundle that has neither.\n */\n\n/**\n * The two representations of a page, prerendered side by side.\n *\n * A hard load wants the HTML document; a soft navigation asks the same URL for a flight payload.\n * Writing only the HTML meant every in-app click re-rendered a page that was already built, so the\n * prerender only ever paid off for cold loads and crawlers.\n */\nexport type PrerenderVariant = 'html' | 'flight';\n\nexport const VARIANTS = {\n html: { file: 'index.html', accept: 'text/html', contentType: 'text/html' },\n flight: { file: 'index.rsc', accept: 'text/x-component', contentType: 'text/x-component' },\n} as const satisfies Record<PrerenderVariant, { file: string; accept: string; contentType: string }>;\n\n/**\n * Where a route's prerendered output lives, relative to the output root — or `null` for a path that\n * cannot be prerendered at all (one with a param or a wildcard left in it).\n *\n * Always `/`-separated, never the host's separator: the same string addresses a file on a filesystem\n * (`resolve()` and `join()` both accept forward slashes on Windows) and a key in an asset store,\n * where a backslash would simply be the wrong character.\n */\nexport function ssgFilePath(routePath: string, variant: PrerenderVariant = 'html'): string | null {\n if (/[:*]/.test(routePath)) return null;\n const trimmed = routePath.replace(/^\\/+|\\/+$/g, '');\n const file = VARIANTS[variant].file;\n return trimmed === '' ? file : `${trimmed}/${file}`;\n}\n\n/**\n * {@link ssgFilePath} for a path that came off a request, so traversal is a miss rather than a lookup.\n *\n * The first line of defence for every deploy target: a store addressed by key has no `resolve()` to\n * fall back on, so `..` has to be refused here or not at all.\n */\nexport function prerenderedRelPath(requestPath: string, variant: PrerenderVariant): string | null {\n if (/(^|\\/)\\.\\.?(\\/|$)/.test(requestPath)) return null;\n return ssgFilePath(requestPath, variant);\n}\n\n/** A prerendered page, ready to serve: its body and a validator derived from those exact bytes. */\nexport interface PrerenderedPage {\n
|
|
1
|
+
{"version":3,"file":"prerendered.js","sourceRoot":"","sources":["../../src/server/prerendered.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,IAAI,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE;IAC3E,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,kBAAkB,EAAE,WAAW,EAAE,kBAAkB,EAAE;CACQ,CAAC;AAErG;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,OAAO,GAAqB,MAAM;IAC/E,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC;IACpC,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,IAAI,EAAE,CAAC;AACtD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,WAAmB,EAAE,OAAyB;IAC/E,IAAI,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC;QAAE,OAAO,IAAI,CAAC;IACvD,OAAO,WAAW,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,GAAG,GAAG,GAAG;IACvC,MAAM,KAAK,GAAG,IAAI,GAAG,EAA2B,CAAC;IACjD,OAAO;QACL,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;QAC5B,GAAG,CAAC,GAAG,EAAE,IAAI;YACX,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACrB,yFAAyF;YACzF,IAAI,KAAK,CAAC,IAAI,GAAG,GAAG;gBAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAM,CAAC,CAAC;QACjE,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAA6B;IAC1D,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC3D,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;SACnE,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACtB,OAAO,MAAM,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC;AACzC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAA6B,EAAE,SAAyB;IAC9F,OAAO;QACL,IAAI;QACJ,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;QACtC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC;KAC7E,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Everything about a prerendered page that doesn't need a filesystem: where the build puts it, and\n * what it looks like once read back.\n *\n * Split out of `ssg.ts` — which owns the writing and the reading, both through `node:fs` — because a\n * deploy target without a filesystem reads the very same layout out of an asset store, and importing\n * it from the fs module would drag `node:fs` and `node:crypto` into a bundle that has neither.\n */\n\n/**\n * The two representations of a page, prerendered side by side.\n *\n * A hard load wants the HTML document; a soft navigation asks the same URL for a flight payload.\n * Writing only the HTML meant every in-app click re-rendered a page that was already built, so the\n * prerender only ever paid off for cold loads and crawlers.\n */\nexport type PrerenderVariant = 'html' | 'flight';\n\nexport const VARIANTS = {\n html: { file: 'index.html', accept: 'text/html', contentType: 'text/html' },\n flight: { file: 'index.rsc', accept: 'text/x-component', contentType: 'text/x-component' },\n} as const satisfies Record<PrerenderVariant, { file: string; accept: string; contentType: string }>;\n\n/**\n * Where a route's prerendered output lives, relative to the output root — or `null` for a path that\n * cannot be prerendered at all (one with a param or a wildcard left in it).\n *\n * Always `/`-separated, never the host's separator: the same string addresses a file on a filesystem\n * (`resolve()` and `join()` both accept forward slashes on Windows) and a key in an asset store,\n * where a backslash would simply be the wrong character.\n */\nexport function ssgFilePath(routePath: string, variant: PrerenderVariant = 'html'): string | null {\n if (/[:*]/.test(routePath)) return null;\n const trimmed = routePath.replace(/^\\/+|\\/+$/g, '');\n const file = VARIANTS[variant].file;\n return trimmed === '' ? file : `${trimmed}/${file}`;\n}\n\n/**\n * {@link ssgFilePath} for a path that came off a request, so traversal is a miss rather than a lookup.\n *\n * The first line of defence for every deploy target: a store addressed by key has no `resolve()` to\n * fall back on, so `..` has to be refused here or not at all.\n */\nexport function prerenderedRelPath(requestPath: string, variant: PrerenderVariant): string | null {\n if (/(^|\\/)\\.\\.?(\\/|$)/.test(requestPath)) return null;\n return ssgFilePath(requestPath, variant);\n}\n\n/**\n * A bounded, insertion-ordered cache of prerendered pages.\n *\n * Bounded so a site with thousands of prerendered pages keeps a working set rather than the whole build\n * in memory. Only *hits* are ever stored: caching misses would let anyone mint entries by requesting\n * paths that don't exist. The files are written at build time and never change while the server is up,\n * so an entry never needs invalidating.\n *\n * Shared because both targets need exactly this and had a hand-rolled copy of it, down to the same\n * eviction line.\n */\nexport function createPageCache(max = 128): { get(key: string): PrerenderedPage | undefined; set(key: string, page: PrerenderedPage): void } {\n const pages = new Map<string, PrerenderedPage>();\n return {\n get: (key) => pages.get(key),\n set(key, page) {\n pages.set(key, page);\n // Insertion-ordered, so the first key is the oldest. One entry in means at most one out.\n if (pages.size > max) pages.delete(pages.keys().next().value!);\n },\n };\n}\n\n/**\n * A weak `ETag` for a page body.\n *\n * Web Crypto rather than `node:crypto`, so the one implementation serves both a Node server and\n * `workerd` — the Cloudflare runtime used to carry its own copy of this, with its own base64url\n * conversion, purely because the other one reached for a Node builtin.\n *\n * Deliberately **weak**: the bytes on the wire depend on whether the client took gzip, and a strong\n * validator would have to differ between those two — so the 200 and the 304 that revalidates it would\n * disagree, and a cache would treat them as different pages. A weak tag says \"the same\n * representation\", which is exactly what is true across content codings.\n */\nexport async function weakEtag(body: Uint8Array<ArrayBuffer>): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', body);\n const base64url = btoa(String.fromCharCode(...new Uint8Array(digest)))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n return `W/\"${base64url.slice(0, 22)}\"`;\n}\n\n/**\n * Assembles a {@link PrerenderedPage} from a body just read out of the build.\n *\n * `storeEtag` is the validator the store supplied, where it has one — it already describes these exact\n * bytes, so it is preferred over hashing them again, and only weakened. Both call sites went through\n * their own copy of this before, with two different digest implementations.\n */\nexport async function toPrerenderedPage(body: Uint8Array<ArrayBuffer>, storeEtag?: string | null): Promise<PrerenderedPage> {\n return {\n body,\n contentLength: String(body.byteLength),\n etag: storeEtag ? storeEtag.replace(/^(?!W\\/)/, 'W/') : await weakEtag(body),\n };\n}\n\n/** A prerendered page, ready to serve: its body and a validator derived from those exact bytes. */\nexport interface PrerenderedPage {\n /**\n * The document or the flight payload, depending on which {@link PrerenderVariant} was read.\n *\n * Bytes rather than a string, because this is a *cache* entry served verbatim to every request that\n * hits it. Held as a string, each of those requests pays a fresh UTF-8 encode of the whole page on the\n * way out — the one route where the framework does no rendering at all was spending its time\n * re-encoding bytes it had already encoded once to measure them. Read once, encoded never.\n */\n body: Uint8Array<ArrayBuffer>;\n /**\n * `Content-Length` for {@link body}, in bytes rather than characters.\n *\n * Served with the response because Hono sets no length for an in-memory body, and a proxy or CDN in\n * front is entitled to make decisions with it — whether gzipping this response is worth the framing,\n * most obviously.\n */\n contentLength: string;\n /**\n * `ETag` for the page, so a revalidating client can be answered with a 304 instead of the body.\n *\n * Deliberately **weak**. The bytes on the wire depend on whether something in front re-encoded them,\n * and a strong validator would have to differ per coding — so the 200 and the 304 that revalidates it\n * could disagree, and a cache would treat them as different pages. A weak tag says \"the same\n * representation\", which is exactly what stays true across content codings.\n */\n etag: string;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RshonoConfig } from '../config.js';
|
|
2
2
|
/**
|
|
3
3
|
* The framework settings the server bundle needs at request time, fully resolved
|
|
4
4
|
* (defaults applied, `bodySizeLimit` parsed to bytes, origins normalized to hosts).
|
|
@@ -15,8 +15,6 @@ export interface ServerConfig {
|
|
|
15
15
|
* produced the bundle, and a deploy target need not have a `process` to read it from.
|
|
16
16
|
*/
|
|
17
17
|
isDev: boolean;
|
|
18
|
-
/** Deadline in ms for a single request (server action + flight + SSR). */
|
|
19
|
-
renderTimeoutMs: number;
|
|
20
18
|
/** Honour `X-Forwarded-Host` / `-Proto` when resolving the browser-facing URL. Forced on in dev. */
|
|
21
19
|
trustProxy: boolean;
|
|
22
20
|
/** Send a strict per-request-nonce Content-Security-Policy with every HTML document. */
|
|
@@ -29,44 +27,39 @@ export interface ServerConfig {
|
|
|
29
27
|
allowedOrigins: string[];
|
|
30
28
|
/** Max request body in bytes before a 413; `0` disables the cap. */
|
|
31
29
|
maxBodyBytes: number;
|
|
32
|
-
/** Gzip compressible responses on the way out. */
|
|
33
|
-
compress: boolean;
|
|
34
|
-
/** Default listen port for `start` (overridden by `PORT`). */
|
|
35
|
-
port?: number;
|
|
36
|
-
/** Default bind address for `start` (overridden by `HOST`). */
|
|
37
|
-
host?: string;
|
|
38
30
|
}
|
|
39
31
|
/**
|
|
40
32
|
* The single source of truth for the framework's built-in defaults.
|
|
41
33
|
*
|
|
42
|
-
* `
|
|
43
|
-
* they
|
|
44
|
-
*
|
|
34
|
+
* `maxBodyBytes` is resolved here, into the bundle. `port` and `host` are not
|
|
35
|
+
* settings at all: they are read from `PORT` / `HOST` (or `--port`) wherever the address is resolved
|
|
36
|
+
* — `deploy/node/runtime.ts` for a server bundle, `cli/dev.ts` for the dev server — and this is the
|
|
37
|
+
* default both fall back to. A config field for them would have been a fourth precedence level for
|
|
38
|
+
* one number, overridden by the environment on every host that matters.
|
|
45
39
|
*/
|
|
46
40
|
export declare const SERVER_DEFAULTS: {
|
|
47
|
-
readonly renderTimeoutMs: 10000;
|
|
48
41
|
readonly maxBodyBytes: number;
|
|
49
42
|
readonly port: 3000;
|
|
50
43
|
readonly host: '0.0.0.0';
|
|
51
44
|
};
|
|
52
45
|
/**
|
|
53
|
-
* The built-in {@link
|
|
54
|
-
* {@link
|
|
46
|
+
* The built-in {@link RshonoConfig.csp} policy, keyed by directive so
|
|
47
|
+
* {@link RshonoConfig.cspDirectives} can override entries individually.
|
|
55
48
|
*
|
|
56
49
|
* `script-src` carries the per-request nonce, appended at request time (see `entry.rsc.tsx`).
|
|
57
50
|
* `style-src` needs `'unsafe-inline'` because React writes inline styles.
|
|
58
51
|
*/
|
|
59
52
|
export declare const CSP_DEFAULTS: Record<string, string>;
|
|
60
|
-
/** Parse a {@link
|
|
53
|
+
/** Parse a {@link RshonoConfig.bodySizeLimit} value into a byte count (`false`/`0` → `0`, disabling the cap). */
|
|
61
54
|
export declare function parseByteSize(value: string | number | false | undefined): number | undefined;
|
|
62
55
|
/**
|
|
63
|
-
* Resolve the user's {@link
|
|
56
|
+
* Resolve the user's {@link RshonoConfig} into the {@link ServerConfig} baked into the bundle.
|
|
64
57
|
*
|
|
65
58
|
* `isDev` is a build-time input rather than a config field because it decides one thing the user
|
|
66
59
|
* shouldn't have to: `trustProxy` is forced on under `rshono dev`, where the framework's own proxy
|
|
67
60
|
* is the only way in (it sets the forwarded headers itself and binds to localhost).
|
|
68
61
|
*/
|
|
69
|
-
export declare function resolveServerConfig(config:
|
|
62
|
+
export declare function resolveServerConfig(config: RshonoConfig, { isDev }: {
|
|
70
63
|
isDev: boolean;
|
|
71
64
|
}): ServerConfig;
|
|
72
65
|
//# sourceMappingURL=server-config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/server/server-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,KAAK,EAAE,OAAO,CAAC;IACf,
|
|
1
|
+
{"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/server/server-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,KAAK,EAAE,OAAO,CAAC;IACf,oGAAoG;IACpG,UAAU,EAAE,OAAO,CAAC;IACpB,wFAAwF;IACxF,UAAU,EAAE,OAAO,CAAC;IACpB,yGAAyG;IACzG,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,gDAAgD;IAChD,WAAW,EAAE,OAAO,CAAC;IACrB,+FAA+F;IAC/F,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe;aAC1B,YAAY;aACZ,IAAI,EAAE,IAAI;aACV,IAAI,EAAE,SAAS;CACP,CAAC;AAEX;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAY/C,CAAC;AAIF,iHAAiH;AACjH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAS5F;AA0BD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,YAAY,CAarG"}
|