Bugfixes. Documentation update.
This commit is contained in:
parent
98d3cf7da7
commit
5c53e94a3e
105
README.md
105
README.md
@ -43,6 +43,9 @@ The goal is a framework that is:
|
|||||||
- [Cache Behavior and Limitations](#cache-behavior-and-limitations)
|
- [Cache Behavior and Limitations](#cache-behavior-and-limitations)
|
||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
- [Middleware](#middleware)
|
- [Middleware](#middleware)
|
||||||
|
- [WebSocket](#websocket)
|
||||||
|
- [Server Options](#server-options)
|
||||||
|
- [Custom Server](#custom-server)
|
||||||
- [Environment Variables](#environment-variables)
|
- [Environment Variables](#environment-variables)
|
||||||
- [How It Works](#how-it-works)
|
- [How It Works](#how-it-works)
|
||||||
- [Development Server](#development-server)
|
- [Development Server](#development-server)
|
||||||
@ -650,6 +653,8 @@ export default config;
|
|||||||
| `development` | `boolean` | — | Overridden to `true` by `bunext dev` automatically |
|
| `development` | `boolean` | — | Overridden to `true` by `bunext dev` automatically |
|
||||||
| `defaultCacheExpiry`| `number` | `3600` | Global page cache expiry in seconds |
|
| `defaultCacheExpiry`| `number` | `3600` | Global page cache expiry in seconds |
|
||||||
| `middleware` | `(params: BunextConfigMiddlewareParams) => Response \| undefined \| Promise<...>` | — | Global middleware — see [Middleware](#middleware) |
|
| `middleware` | `(params: BunextConfigMiddlewareParams) => Response \| undefined \| Promise<...>` | — | Global middleware — see [Middleware](#middleware) |
|
||||||
|
| `websocket` | `WebSocketHandler<any>` | — | Bun WebSocket handler — see [WebSocket](#websocket) |
|
||||||
|
| `serverOptions` | `ServeOptions` | — | Extra options passed to `Bun.serve()` (excluding `fetch`) — see [Server Options](#server-options) |
|
||||||
|
|
||||||
### Middleware
|
### Middleware
|
||||||
|
|
||||||
@ -666,7 +671,7 @@ import type {
|
|||||||
} from "bunext/src/types";
|
} from "bunext/src/types";
|
||||||
|
|
||||||
const config: BunextConfig = {
|
const config: BunextConfig = {
|
||||||
middleware: async ({ req, url, server }) => {
|
middleware: async ({ req, url }) => {
|
||||||
// Example: protect all /dashboard/* routes
|
// Example: protect all /dashboard/* routes
|
||||||
if (url.pathname.startsWith("/dashboard")) {
|
if (url.pathname.startsWith("/dashboard")) {
|
||||||
const token = req.headers.get("authorization");
|
const token = req.headers.get("authorization");
|
||||||
@ -697,6 +702,104 @@ middleware: async ({ req, url }) => {
|
|||||||
},
|
},
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### WebSocket
|
||||||
|
|
||||||
|
Add a `websocket` field to `bunext.config.ts` to handle WebSocket connections. The value is passed directly to `Bun.serve()` as the `websocket` option and accepts the full [`WebSocketHandler`](https://bun.sh/docs/api/websockets) interface.
|
||||||
|
|
||||||
|
Define the handler in its own file and import it into the config:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// websocket.ts
|
||||||
|
import type { WebSocketHandler } from "bun";
|
||||||
|
|
||||||
|
export const BunextWebsocket: WebSocketHandler<any> = {
|
||||||
|
message(ws, message) {
|
||||||
|
console.log(`WS Message => ${message}`);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// bunext.config.ts
|
||||||
|
import type { BunextConfig } from "bunext/src/types";
|
||||||
|
import { BunextWebsocket } from "./websocket";
|
||||||
|
|
||||||
|
const config: BunextConfig = {
|
||||||
|
websocket: BunextWebsocket,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Server Options
|
||||||
|
|
||||||
|
Pass additional options to the underlying `Bun.serve()` call via `serverOptions`. All standard [`ServeOptions`](https://bun.sh/docs/api/http) fields are accepted except `fetch`, which Bunext manages internally.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// bunext.config.ts
|
||||||
|
import type { BunextConfig } from "bunext/src/types";
|
||||||
|
|
||||||
|
const config: BunextConfig = {
|
||||||
|
serverOptions: {
|
||||||
|
tls: {
|
||||||
|
cert: Bun.file("./certs/cert.pem"),
|
||||||
|
key: Bun.file("./certs/key.pem"),
|
||||||
|
},
|
||||||
|
maxRequestBodySize: 64 * 1024 * 1024, // 64 MB
|
||||||
|
error(err) {
|
||||||
|
console.error("Server error:", err);
|
||||||
|
return new Response("Internal Server Error", { status: 500 });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Custom Server
|
||||||
|
|
||||||
|
For full control over the `Bun.serve()` instance — custom WebSocket upgrade logic, multi-protocol handling, or integrating Bunext alongside other handlers — you can skip `bunext dev` / `bunext start` and run your own server using Bunext's exported primitives.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// server.ts
|
||||||
|
import bunext from "bunext";
|
||||||
|
|
||||||
|
const development = process.env.NODE_ENV === "development";
|
||||||
|
const port = process.env.PORT || 3700;
|
||||||
|
|
||||||
|
await bunext.bunextInit();
|
||||||
|
|
||||||
|
const server = Bun.serve({
|
||||||
|
routes: {
|
||||||
|
"/*": {
|
||||||
|
async GET(req) {
|
||||||
|
return await bunext.bunextRequestHandler({ req });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
development,
|
||||||
|
port,
|
||||||
|
});
|
||||||
|
|
||||||
|
bunext.bunextLog.info(`Server running on http://localhost:${server.port} ...`);
|
||||||
|
```
|
||||||
|
|
||||||
|
| Export | Type | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| `bunextInit()` | `() => Promise<void>` | Initializes config, router, and bundler. Must be called before handling requests. |
|
||||||
|
| `bunextRequestHandler({ req })` | `(params: { req: Request }) => Promise<Response>` | The main Bunext request dispatcher — middleware, routing, SSR, static files. Only `req` is needed; the server instance is managed internally. |
|
||||||
|
| `bunextLog` | Logger | Framework logger (`info`, `error`, `success`, `server`, `watch`). |
|
||||||
|
|
||||||
|
Run the custom server directly with Bun:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run server.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note:** When using a custom server, HMR and file watching are still driven by `bunextInit()`. Pass `development: true` in your `Bun.serve()` call to enable them.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|||||||
@ -134,6 +134,10 @@ This report compares the two on their overlapping surface — server-side render
|
|||||||
| Prefetching | — | ✅ | ✅ |
|
| Prefetching | — | ✅ | ✅ |
|
||||||
| `useRouter` / `usePathname` hooks | — | ✅ | ✅ |
|
| `useRouter` / `usePathname` hooks | — | ✅ | ✅ |
|
||||||
| `useSearchParams` hook | — | ✅ | ✅ |
|
| `useSearchParams` hook | — | ✅ | ✅ |
|
||||||
|
| **Server** | | | |
|
||||||
|
| WebSocket support | ✅ | ❌ | ❌ |
|
||||||
|
| Custom server (bring your own `Bun.serve()`) | ✅ | ✅ (custom `server.js`) | ✅ |
|
||||||
|
| Extra server options (`tls`, `error`, etc.) | ✅ (`serverOptions`) | ✅ | ✅ |
|
||||||
| **Deployment** | | | |
|
| **Deployment** | | | |
|
||||||
| Self-hosted (any server with runtime) | ✅ | ✅ | ✅ |
|
| Self-hosted (any server with runtime) | ✅ | ✅ | ✅ |
|
||||||
| No vendor lock-in | ✅ | ❌ (Vercel-optimised) | ❌ |
|
| No vendor lock-in | ✅ | ❌ (Vercel-optimised) | ❌ |
|
||||||
@ -358,7 +362,7 @@ The key distinction from SSG: Bunext's cache is **demand-driven**. A site with 1
|
|||||||
| `Request` | *(planned)* Replaces the request and continues through the pipeline |
|
| `Request` | *(planned)* Replaces the request and continues through the pipeline |
|
||||||
| `undefined` | Passes through unchanged |
|
| `undefined` | Passes through unchanged |
|
||||||
|
|
||||||
The planned `Request` return allows header injection, auth token forwarding, locale detection, or any other request mutation without terminating the pipeline — a clean, standard-API alternative to Next.js's custom `NextResponse.next({ headers: ... })` pattern.
|
The planned `Request` return allows header injection, auth token forwarding, locale detection, or any other request mutation without terminating the pipeline — a clean, standard-API alternative to Next.js's custom `NextResponse.next({ headers: ... })` pattern. The middleware function receives `{ req, url }`. The server instance is managed internally and does not need to be passed explicitly.
|
||||||
|
|
||||||
**Next.js** middleware runs per-request in a lightweight Edge Runtime (V8 isolate), with matched route patterns configured via `matcher`. It uses `NextRequest`/`NextResponse` extensions for rewriting, redirecting, and injecting headers without returning a full response.
|
**Next.js** middleware runs per-request in a lightweight Edge Runtime (V8 isolate), with matched route patterns configured via `matcher`. It uses `NextRequest`/`NextResponse` extensions for rewriting, redirecting, and injecting headers without returning a full response.
|
||||||
|
|
||||||
@ -426,7 +430,7 @@ Next.js provides additional type-safety features:
|
|||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
**Bunext** (`bunext.config.ts`) exposes: `port`, `origin`, `distDir`, `assetsPrefix`, `globalVars`, `development`, `defaultCacheExpiry`, `middleware`.
|
**Bunext** (`bunext.config.ts`) exposes: `port`, `origin`, `distDir`, `assetsPrefix`, `globalVars`, `development`, `defaultCacheExpiry`, `middleware`, `websocket`, `serverOptions`.
|
||||||
|
|
||||||
**Next.js** (`next.config.js`) additionally supports:
|
**Next.js** (`next.config.js`) additionally supports:
|
||||||
- `redirects()` — array of redirect rules evaluated at the server level.
|
- `redirects()` — array of redirect rules evaluated at the server level.
|
||||||
@ -592,6 +596,39 @@ Bunext runs on any server where Bun is installed. There is no Vercel platform de
|
|||||||
|
|
||||||
Next.js is technically self-hostable but is architecturally optimised for Vercel — features like ISR, image optimisation, Edge Middleware, and Analytics are either Vercel-only or degraded outside it.
|
Next.js is technically self-hostable but is architecturally optimised for Vercel — features like ISR, image optimisation, Edge Middleware, and Analytics are either Vercel-only or degraded outside it.
|
||||||
|
|
||||||
|
### WebSocket and Custom Server
|
||||||
|
|
||||||
|
Bunext ships native WebSocket support via a `websocket` field in `bunext.config.ts`. The value is a Bun `WebSocketHandler` passed directly to `Bun.serve()` — no third-party library, no adapter, no separate process. Upgrade requests are triggered from any API route — only `req` is needed, as the server instance is managed internally by the framework.
|
||||||
|
|
||||||
|
Next.js has no built-in WebSocket support. Upgrading a connection requires a custom Node.js server (`server.js`) outside the Next.js framework, which loses access to Next.js's built-in routing and middleware for that connection.
|
||||||
|
|
||||||
|
For projects that need full control over `Bun.serve()` — custom TLS, multi-protocol handling, integrating Bunext alongside other handlers — Bunext exports `bunextInit()` and `bunextRequestHandler()` as first-class primitives. The developer owns the server; Bunext handles the request processing:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// server.ts
|
||||||
|
import bunext from "bunext";
|
||||||
|
|
||||||
|
const development = process.env.NODE_ENV === "development";
|
||||||
|
|
||||||
|
await bunext.bunextInit();
|
||||||
|
|
||||||
|
const server = Bun.serve({
|
||||||
|
routes: {
|
||||||
|
"/*": {
|
||||||
|
async GET(req) {
|
||||||
|
return await bunext.bunextRequestHandler({ req });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
development,
|
||||||
|
port: process.env.PORT || 3700,
|
||||||
|
});
|
||||||
|
|
||||||
|
bunext.bunextLog.info(`Server running on http://localhost:${server.port} ...`);
|
||||||
|
```
|
||||||
|
|
||||||
|
Next.js requires a `server.js` file that wraps `next()` — a documented but officially discouraged pattern that disables some platform-specific features on Vercel.
|
||||||
|
|
||||||
### Bun-Native APIs
|
### Bun-Native APIs
|
||||||
|
|
||||||
Server functions and API routes have direct access to Bun's native APIs: `Bun.file()`, `Bun.write()`, the native SQLite driver, `Bun.password`, `Bun.serve` WebSocket support, etc. — without any Node.js compatibility shim or polyfill layer.
|
Server functions and API routes have direct access to Bun's native APIs: `Bun.file()`, `Bun.write()`, the native SQLite driver, `Bun.password`, `Bun.serve` WebSocket support, etc. — without any Node.js compatibility shim or polyfill layer.
|
||||||
|
|||||||
10
dist/functions/server/handle-hmr.js
vendored
10
dist/functions/server/handle-hmr.js
vendored
@ -8,6 +8,7 @@ export default async function ({ req }) {
|
|||||||
? global.BUNDLER_CTX_MAP?.find((m) => m.local_path == match.filePath)
|
? global.BUNDLER_CTX_MAP?.find((m) => m.local_path == match.filePath)
|
||||||
: undefined;
|
: undefined;
|
||||||
let controller;
|
let controller;
|
||||||
|
let heartbeat;
|
||||||
const stream = new ReadableStream({
|
const stream = new ReadableStream({
|
||||||
start(c) {
|
start(c) {
|
||||||
controller = c;
|
controller = c;
|
||||||
@ -16,8 +17,17 @@ export default async function ({ req }) {
|
|||||||
page_url: referer_url.href,
|
page_url: referer_url.href,
|
||||||
target_map,
|
target_map,
|
||||||
});
|
});
|
||||||
|
heartbeat = setInterval(() => {
|
||||||
|
try {
|
||||||
|
c.enqueue(": keep-alive\n\n");
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
clearInterval(heartbeat);
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
},
|
},
|
||||||
cancel() {
|
cancel() {
|
||||||
|
clearInterval(heartbeat);
|
||||||
const targetControllerIndex = global.HMR_CONTROLLERS.findIndex((c) => c.controller == controller);
|
const targetControllerIndex = global.HMR_CONTROLLERS.findIndex((c) => c.controller == controller);
|
||||||
if (typeof targetControllerIndex == "number" &&
|
if (typeof targetControllerIndex == "number" &&
|
||||||
targetControllerIndex >= 0) {
|
targetControllerIndex >= 0) {
|
||||||
|
|||||||
@ -19,6 +19,7 @@ export default async function (params) {
|
|||||||
script += `window.addEventListener("error", (e) => __bunext_show_error(e.message, e.filename ? e.filename + ":" + e.lineno + ":" + e.colno : "", e.error?.stack ?? ""));\n`;
|
script += `window.addEventListener("error", (e) => __bunext_show_error(e.message, e.filename ? e.filename + ":" + e.lineno + ":" + e.colno : "", e.error?.stack ?? ""));\n`;
|
||||||
script += `window.addEventListener("unhandledrejection", (e) => __bunext_show_error(String(e.reason?.message ?? e.reason), "", e.reason?.stack ?? ""));\n\n`;
|
script += `window.addEventListener("unhandledrejection", (e) => __bunext_show_error(String(e.reason?.message ?? e.reason), "", e.reason?.stack ?? ""));\n\n`;
|
||||||
script += `const hmr = new EventSource("/__hmr");\n`;
|
script += `const hmr = new EventSource("/__hmr");\n`;
|
||||||
|
script += `window.BUNEXT_HMR = hmr;\n`;
|
||||||
script += `window.addEventListener("beforeunload", () => hmr.close());\n`;
|
script += `window.addEventListener("beforeunload", () => hmr.close());\n`;
|
||||||
script += `hmr.addEventListener("update", async (event) => {\n`;
|
script += `hmr.addEventListener("update", async (event) => {\n`;
|
||||||
script += ` if (event?.data) {\n`;
|
script += ` if (event?.data) {\n`;
|
||||||
|
|||||||
@ -19,11 +19,6 @@ const server = Bun.serve({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* Set this to prevent HMR timeout warnings in the
|
|
||||||
* browser console in development mode.
|
|
||||||
*/
|
|
||||||
idleTimeout: development ? 0 : undefined,
|
|
||||||
development,
|
development,
|
||||||
port,
|
port,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -21,7 +21,7 @@ The full return contract:
|
|||||||
```ts
|
```ts
|
||||||
// bunext.config.ts
|
// bunext.config.ts
|
||||||
const config: BunextConfig = {
|
const config: BunextConfig = {
|
||||||
middleware: async ({ req, url, server }) => {
|
middleware: async ({ req, url }) => {
|
||||||
// Inject an auth header and continue
|
// Inject an auth header and continue
|
||||||
const token = await verifySession(req);
|
const token = await verifySession(req);
|
||||||
if (token) {
|
if (token) {
|
||||||
@ -49,11 +49,36 @@ const config: BunextConfig = {
|
|||||||
|
|
||||||
## Custom Server
|
## Custom Server
|
||||||
|
|
||||||
**Status:** In development
|
**Status:** Shipped
|
||||||
|
|
||||||
Allow consumer projects to create and fully customize the underlying `Bun.serve()` instance. Instead of Bunext owning the server entirely, the developer can provide their own server setup and integrate Bunext's request handler into it.
|
Consumer projects can create and fully customize the underlying `Bun.serve()` instance by using Bunext's exported primitives directly. Instead of Bunext owning the server, the developer provides their own `Bun.serve()` call and integrates Bunext's request handler into it.
|
||||||
|
|
||||||
This enables use cases that require low-level server control:
|
```ts
|
||||||
|
import bunext from "bunext";
|
||||||
|
|
||||||
|
await bunext.bunextInit();
|
||||||
|
|
||||||
|
const server = Bun.serve({
|
||||||
|
routes: {
|
||||||
|
"/*": {
|
||||||
|
async GET(req) {
|
||||||
|
return await bunext.bunextRequestHandler({ req });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
port: 3000,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Exported primitives:
|
||||||
|
|
||||||
|
| Export | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `bunextInit()` | Initializes config, router, and bundler |
|
||||||
|
| `bunextRequestHandler({ req })` | Main Bunext request dispatcher |
|
||||||
|
| `bunextLog` | Framework logger |
|
||||||
|
|
||||||
|
Use cases:
|
||||||
- Custom WebSocket upgrade handling
|
- Custom WebSocket upgrade handling
|
||||||
- Custom TLS/SSL configuration
|
- Custom TLS/SSL configuration
|
||||||
- Integrating Bunext into an existing Bun server alongside other handlers
|
- Integrating Bunext into an existing Bun server alongside other handlers
|
||||||
@ -83,31 +108,31 @@ A server is a fundamental requirement for Bunext — like WordPress, it is desig
|
|||||||
|
|
||||||
## WebSocket Support via Config
|
## WebSocket Support via Config
|
||||||
|
|
||||||
**Status:** Planned
|
**Status:** Shipped
|
||||||
|
|
||||||
Add a `websocket` parameter to `bunext.config.ts` to handle WebSocket connections without requiring a custom server. This gives most projects a zero-config path to WebSockets while the custom server feature covers advanced use cases.
|
The `websocket` field in `bunext.config.ts` accepts a Bun [`WebSocketHandler`](https://bun.sh/docs/api/websockets) and passes it directly to `Bun.serve()`. This gives most projects a zero-config path to WebSockets; the custom server feature covers advanced upgrade routing.
|
||||||
|
|
||||||
Proposed config shape:
|
```ts
|
||||||
|
// websocket.ts
|
||||||
|
import type { WebSocketHandler } from "bun";
|
||||||
|
|
||||||
|
export const BunextWebsocket: WebSocketHandler<any> = {
|
||||||
|
message(ws, message) {
|
||||||
|
console.log(`WS Message => ${message}`);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// bunext.config.ts
|
// bunext.config.ts
|
||||||
import type { BunextConfig } from "bunext/src/types";
|
import type { BunextConfig } from "bunext/src/types";
|
||||||
|
import { BunextWebsocket } from "./websocket";
|
||||||
|
|
||||||
const config: BunextConfig = {
|
const config: BunextConfig = {
|
||||||
websocket: {
|
websocket: BunextWebsocket,
|
||||||
message(ws, message) {
|
|
||||||
ws.send(`echo: ${message}`);
|
|
||||||
},
|
|
||||||
open(ws) {
|
|
||||||
console.log("Client connected");
|
|
||||||
},
|
|
||||||
close(ws, code, reason) {
|
|
||||||
console.log("Client disconnected");
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
```
|
```
|
||||||
|
|
||||||
The `websocket` field maps directly to Bun's [`WebSocketHandler`](https://bun.sh/docs/api/websockets) interface, passed through to `Bun.serve()`. WebSocket upgrade requests are handled automatically by the framework before the normal request pipeline runs.
|
A companion `serverOptions` field is also available to pass any other `Bun.serve()` options (TLS, error handler, `maxRequestBodySize`, etc.) without needing a custom server.
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "tsc --watch",
|
"dev": "tsc --watch",
|
||||||
"publish": "tsc --noEmit && tsc && git add . && git commit -m 'Update Version' && git push",
|
"publish": "tsc --noEmit && tsc && git add . && git commit -m 'Bugfixes. Documentation update.' && git push",
|
||||||
"compile": "bun build ./src/commands/index.ts --compile --outfile bin/bunext",
|
"compile": "bun build ./src/commands/index.ts --compile --outfile bin/bunext",
|
||||||
"build": "tsc"
|
"build": "tsc"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -17,6 +17,7 @@ export default async function ({ req }: Params): Promise<Response> {
|
|||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
let controller: ReadableStreamDefaultController<string>;
|
let controller: ReadableStreamDefaultController<string>;
|
||||||
|
let heartbeat: ReturnType<typeof setInterval>;
|
||||||
const stream = new ReadableStream<string>({
|
const stream = new ReadableStream<string>({
|
||||||
start(c) {
|
start(c) {
|
||||||
controller = c;
|
controller = c;
|
||||||
@ -25,8 +26,16 @@ export default async function ({ req }: Params): Promise<Response> {
|
|||||||
page_url: referer_url.href,
|
page_url: referer_url.href,
|
||||||
target_map,
|
target_map,
|
||||||
});
|
});
|
||||||
|
heartbeat = setInterval(() => {
|
||||||
|
try {
|
||||||
|
c.enqueue(": keep-alive\n\n");
|
||||||
|
} catch {
|
||||||
|
clearInterval(heartbeat);
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
},
|
},
|
||||||
cancel() {
|
cancel() {
|
||||||
|
clearInterval(heartbeat);
|
||||||
const targetControllerIndex = global.HMR_CONTROLLERS.findIndex(
|
const targetControllerIndex = global.HMR_CONTROLLERS.findIndex(
|
||||||
(c) => c.controller == controller,
|
(c) => c.controller == controller,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -28,6 +28,7 @@ export default async function (params?: Params) {
|
|||||||
script += `window.addEventListener("unhandledrejection", (e) => __bunext_show_error(String(e.reason?.message ?? e.reason), "", e.reason?.stack ?? ""));\n\n`;
|
script += `window.addEventListener("unhandledrejection", (e) => __bunext_show_error(String(e.reason?.message ?? e.reason), "", e.reason?.stack ?? ""));\n\n`;
|
||||||
|
|
||||||
script += `const hmr = new EventSource("/__hmr");\n`;
|
script += `const hmr = new EventSource("/__hmr");\n`;
|
||||||
|
script += `window.BUNEXT_HMR = hmr;\n`;
|
||||||
script += `window.addEventListener("beforeunload", () => hmr.close());\n`;
|
script += `window.addEventListener("beforeunload", () => hmr.close());\n`;
|
||||||
script += `hmr.addEventListener("update", async (event) => {\n`;
|
script += `hmr.addEventListener("update", async (event) => {\n`;
|
||||||
script += ` if (event?.data) {\n`;
|
script += ` if (event?.data) {\n`;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user