1.4.0: not_found, server-only response options, compression

- Server functions can return not_found: true to render the 404 page
  with status 404.
- Page components and window.__PAGE_PROPS__ (and HMR updates) only get
  props, query, url and error. response_options, redirect, cache settings
  and res_transform stay on the server, so cookies and private headers no
  longer end up in the HTML. BunextPageProps is narrowed to match
  (breaking for code that read server-only fields from component props).
- response_options headers go through a Headers object, so repeated
  headers such as several Set-Cookie survive.
- Redirects: status_code wins over permanent; response_options headers
  and res_transform apply to redirects too.
- Gzip for compressible text responses; compress: false to turn it off.
- Files the router matches but never bundles (.server.ts, __root,
  excluded paths and directories) return 404 instead of looping through
  dev rebuild retries.
- Missing public files return 404 rather than 403, so dotted page paths
  like /users/john.doe fall through to pages again.
- importFresh detects rewrites within the filesystem's mtime granularity.
- README: self-hosted React (not esm.sh), snake_case option names, static
  file serving, cache headers, the new options.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-11-09 04:14:56 +01:00
co-authored by Claude Opus 5.5
parent b410b92f9f
commit 96e392f556
79 changed files with 1334 additions and 184 deletions
+35 -3
View File
@@ -36,6 +36,12 @@ export type PageModule = {
staticParams: StaticParams;
};
export type BunextConfig = {
/**
* Build artifact directory, relative to the project root. Defaults to
* `.bunext`. Must be inside the project root, outside `src/`, `public/`
* and `node_modules/`. Overridden by the `BUNEXT_DIST_DIR` env var and
* by `--dist-dir` on the CLI.
*/
dist_dir?: string;
assets_prefix?: string;
origin?: string;
@@ -63,6 +69,12 @@ export type BunextConfig = {
* reverse proxy that sets it). Defaults to `false`.
*/
trust_proxy?: boolean;
/**
* Gzip text responses (HTML, JS, CSS, JSON, SVG, ...) when the client
* accepts it. Defaults to `true`; set `false` when a reverse proxy
* already compresses.
*/
compress?: boolean;
websocket?: WebSocketHandler<any>;
server_options?: Omit<Bun.Serve.Options<any>, "fetch" | "routes">;
/**
@@ -197,7 +209,7 @@ export type BunextServerRouteConfig = {
max_request_body_mb?: number;
};
export type LivePageDistGenParams = {
component?: FC;
component?: FC<any>;
pageProps?: any;
module?: BunextPageModule;
root_module?: BunextRootModule;
@@ -294,6 +306,17 @@ export type BunextPageModuleServerReturn<T extends {
props?: T;
query?: Q;
redirect?: BunextPageModuleServerRedirect;
/**
* Render the 404 page with status 404 instead of this page, as if the
* route did not exist. Ignored when `redirect` is set.
*/
not_found?: boolean;
/**
* Status and headers of the HTML response. Server-only: never sent to
* the browser or passed to the page and root components. A `Headers`
* object or `[name, value][]` keeps repeated headers (e.g. several
* `Set-Cookie`); a plain object can hold each name once.
*/
response_options?: ResponseInit;
/**
* Whether to cache the current page
@@ -311,6 +334,12 @@ export type BunextCachePageType = boolean | ((params: {
ctx: Omit<BunxRouteParams, "body">;
serverRes?: BunextPageModuleServerReturn;
}) => Promise<boolean> | boolean);
/**
* What the page and root components receive, and what is serialized into
* the HTML for hydration: the client-safe part of the server function's
* result. Server-only fields (`response_options`, `redirect`, cache
* settings, `res_transform`) are never included.
*/
export type BunextPageProps<T extends {
[k: string]: any;
} = {
@@ -319,7 +348,7 @@ export type BunextPageProps<T extends {
[k: string]: any;
} = {
[k: string]: any;
}> = BunextPageModuleServerReturn<T, Q>;
}> = Pick<BunextPageModuleServerReturn<T, Q>, "props" | "query" | "url" | "error">;
export type BunextPageModuleServerReturnURLObject = {
hash: string;
host: string;
@@ -336,7 +365,9 @@ export type BunextPageModuleServerReturnURLObject = {
};
export type BunextPageModuleServerRedirect = {
destination: string;
/** 301 instead of the default 302. Ignored when `status_code` is set. */
permanent?: boolean;
/** e.g. 307/308 to keep the request method. Wins over `permanent`. */
status_code?: number;
};
export type BunextPageModuleMetadata = {
@@ -468,7 +499,8 @@ export type BunextGenerateSitemapParams = {
default_lastmod?: Date | string;
/**
* Page last-modified record written by `bunext build`, relative to the
* project root. Defaults to `.bunext/page-lastmod.json`. Point it at a
* project root. Defaults to `page-lastmod.json` in the dist directory
* (`.bunext/page-lastmod.json`). Point it at a
* cached or committed location when builds start from a fresh checkout.
*/
lastmod_file?: string;