From 471ae43457bbc99ada7e743306263a4a55d8d487 Mon Sep 17 00:00:00 2001 From: Sunil Pai Date: Sun, 22 Feb 2026 18:38:07 +0000 Subject: [PATCH] Expose Hono context to Party callbacks Pass the Hono Context (c) as a third argument to onBeforeConnect/onBeforeRequest callbacks so handlers can access c.env, c.var, c.get(), etc. Adds a .changeset for a patch release, updates the Hono fixture and README to demonstrate using env bindings, and implements HonoPartyServerOptions plus wrapOptionsWithContext to close over the Hono context. Also adjusts middleware and handler wiring (createMiddleware generic, options wrapping, and handler signatures) to forward the context to partyserver callbacks. --- .changeset/expose-hono-context.md | 5 +++ fixtures/hono/src/server.ts | 24 ++++++++-- packages/hono-party/README.md | 10 +++-- packages/hono-party/src/index.ts | 74 +++++++++++++++++++++++++------ 4 files changed, 92 insertions(+), 21 deletions(-) create mode 100644 .changeset/expose-hono-context.md diff --git a/.changeset/expose-hono-context.md b/.changeset/expose-hono-context.md new file mode 100644 index 00000000..8704547f --- /dev/null +++ b/.changeset/expose-hono-context.md @@ -0,0 +1,5 @@ +--- +"hono-party": patch +--- + +Expose Hono context as a third argument to `onBeforeConnect` and `onBeforeRequest` callbacks, giving access to `c.env`, `c.var`, `c.get()`, etc. diff --git a/fixtures/hono/src/server.ts b/fixtures/hono/src/server.ts index 1501643e..733fdd63 100644 --- a/fixtures/hono/src/server.ts +++ b/fixtures/hono/src/server.ts @@ -4,7 +4,10 @@ import { Server } from "partyserver"; import type { Connection, WSMessage } from "partyserver"; -// Multiple party servers +type Bindings = { + Chat: DurableObjectNamespace; +}; + export class Chat extends Server { onMessage(connection: Connection, message: WSMessage): void | Promise { console.log("onMessage", message); @@ -12,8 +15,23 @@ export class Chat extends Server { } } -const app = new Hono(); -app.use("*", partyserverMiddleware()); +const app = new Hono<{ Bindings: Bindings }>(); + +app.use( + "*", + partyserverMiddleware<{ Bindings: Bindings }>({ + options: { + onBeforeConnect(req, lobby, c) { + const url = new URL(req.url); + const token = url.searchParams.get("token"); + if (!token) { + return new Response("Unauthorized", { status: 401 }); + } + console.log("env bindings available:", Object.keys(c.env)); + } + } + }) +); app.get("/", (c) => c.text("Hello from Hono!")); diff --git a/packages/hono-party/README.md b/packages/hono-party/README.md index 6e37b374..60111f89 100644 --- a/packages/hono-party/README.md +++ b/packages/hono-party/README.md @@ -24,14 +24,16 @@ export class Document extends Server {} const app = new Hono(); app.use("*", partyserverMiddleware()); -// or with authentication +// or with authentication (using env bindings via Hono context) +type Env = { Bindings: { JWT_SECRET: string } }; app.use( "*", - partyserverMiddleware({ + partyserverMiddleware({ options: { - onBeforeConnect: async (req) => { + onBeforeConnect: async (req, lobby, c) => { const token = req.headers.get("authorization"); - // validate token + const secret = c.env.JWT_SECRET; + // validate token against secret if (!token) return new Response("Unauthorized", { status: 401 }); } } diff --git a/packages/hono-party/src/index.ts b/packages/hono-party/src/index.ts index edfdacc1..74dc568f 100644 --- a/packages/hono-party/src/index.ts +++ b/packages/hono-party/src/index.ts @@ -3,14 +3,39 @@ import { createMiddleware } from "hono/factory"; import { routePartykitRequest } from "partyserver"; import type { Context, Env } from "hono"; -import type { PartyServerOptions } from "partyserver"; +import type { Lobby, PartyServerOptions } from "partyserver"; + +/** + * Extended options for the Hono middleware that pass the Hono context + * to `onBeforeConnect` and `onBeforeRequest` as a third argument, + * giving access to `c.env`, `c.var`, `c.get()`, etc. + */ +export type HonoPartyServerOptions = Omit< + PartyServerOptions, + "onBeforeConnect" | "onBeforeRequest" +> & { + onBeforeConnect?: ( + req: Request, + lobby: Lobby, + c: Context + ) => Response | Request | void | Promise; + onBeforeRequest?: ( + req: Request, + lobby: Lobby, + c: Context + ) => + | Response + | Request + | void + | Promise; +}; /** * Configuration options for the PartyServer middleware */ type PartyServerMiddlewareContext = { /** PartyServer-specific configuration options */ - options?: PartyServerOptions; + options?: HonoPartyServerOptions; /** Optional error handler for caught errors */ onError?: (error: Error) => void; }; @@ -22,12 +47,12 @@ type PartyServerMiddlewareContext = { export function partyserverMiddleware( ctx?: PartyServerMiddlewareContext ) { - return createMiddleware(async (c, next) => { + return createMiddleware(async (c, next) => { try { - const handler = isWebSocketUpgrade(c) - ? handleWebSocketUpgrade - : handleHttpRequest; - const response = await handler(c, ctx?.options); + const options = wrapOptionsWithContext(ctx?.options, c); + const response = isWebSocketUpgrade(c) + ? await handleWebSocketUpgrade(c, options) + : await handleHttpRequest(c, options); return response === null ? await next() : response; } catch (error) { @@ -40,6 +65,30 @@ export function partyserverMiddleware( }); } +/** + * Wraps the Hono-specific options into standard PartyServerOptions by + * closing over the Hono context so callbacks receive it as a third arg. + */ +function wrapOptionsWithContext( + options: HonoPartyServerOptions | undefined, + c: Context +): PartyServerOptions | undefined { + if (!options) return undefined; + + const { onBeforeConnect, onBeforeRequest, ...rest } = options; + return { + ...rest, + ...(onBeforeConnect && { + onBeforeConnect: (req: Request, lobby: Lobby) => + onBeforeConnect(req, lobby, c) + }), + ...(onBeforeRequest && { + onBeforeRequest: (req: Request, lobby: Lobby) => + onBeforeRequest(req, lobby, c) + }) + }; +} + /** * Checks if the incoming request is a WebSocket upgrade request * Looks for the 'upgrade' header with a value of 'websocket' (case-insensitive) @@ -60,9 +109,9 @@ function createRequestFromContext(c: Context) { * Handles WebSocket upgrade requests * Returns a WebSocket upgrade response if successful, null otherwise */ -async function handleWebSocketUpgrade( - c: Context, - options?: PartyServerOptions +async function handleWebSocketUpgrade( + c: Context, + options?: PartyServerOptions ) { const req = createRequestFromContext(c); const response = await routePartykitRequest(req, env(c), options); @@ -81,10 +130,7 @@ async function handleWebSocketUpgrade( * Handles standard HTTP requests * Forwards the request to PartyServer and returns the response */ -async function handleHttpRequest( - c: Context, - options?: PartyServerOptions -) { +async function handleHttpRequest(c: Context, options?: PartyServerOptions) { const req = createRequestFromContext(c); return routePartykitRequest(req, env(c), options); }