Skip to content

Commit d1906a9

Browse files
committed
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.
1 parent e3731d3 commit d1906a9

4 files changed

Lines changed: 92 additions & 21 deletions

File tree

.changeset/expose-hono-context.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"hono-party": patch
3+
---
4+
5+
Expose Hono context as a third argument to `onBeforeConnect` and `onBeforeRequest` callbacks, giving access to `c.env`, `c.var`, `c.get()`, etc.

fixtures/hono/src/server.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,34 @@ import { Server } from "partyserver";
44

55
import type { Connection, WSMessage } from "partyserver";
66

7-
// Multiple party servers
7+
type Bindings = {
8+
Chat: DurableObjectNamespace;
9+
};
10+
811
export class Chat extends Server {
912
onMessage(connection: Connection, message: WSMessage): void | Promise<void> {
1013
console.log("onMessage", message);
1114
this.broadcast(message, [connection.id]);
1215
}
1316
}
1417

15-
const app = new Hono();
16-
app.use("*", partyserverMiddleware());
18+
const app = new Hono<{ Bindings: Bindings }>();
19+
20+
app.use(
21+
"*",
22+
partyserverMiddleware<{ Bindings: Bindings }>({
23+
options: {
24+
onBeforeConnect(req, lobby, c) {
25+
const url = new URL(req.url);
26+
const token = url.searchParams.get("token");
27+
if (!token) {
28+
return new Response("Unauthorized", { status: 401 });
29+
}
30+
console.log("env bindings available:", Object.keys(c.env));
31+
}
32+
}
33+
})
34+
);
1735

1836
app.get("/", (c) => c.text("Hello from Hono!"));
1937

packages/hono-party/README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,16 @@ export class Document extends Server {}
2424
const app = new Hono();
2525
app.use("*", partyserverMiddleware());
2626

27-
// or with authentication
27+
// or with authentication (using env bindings via Hono context)
28+
type Env = { Bindings: { JWT_SECRET: string } };
2829
app.use(
2930
"*",
30-
partyserverMiddleware({
31+
partyserverMiddleware<Env>({
3132
options: {
32-
onBeforeConnect: async (req) => {
33+
onBeforeConnect: async (req, lobby, c) => {
3334
const token = req.headers.get("authorization");
34-
// validate token
35+
const secret = c.env.JWT_SECRET;
36+
// validate token against secret
3537
if (!token) return new Response("Unauthorized", { status: 401 });
3638
}
3739
}

packages/hono-party/src/index.ts

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,39 @@ import { createMiddleware } from "hono/factory";
33
import { routePartykitRequest } from "partyserver";
44

55
import type { Context, Env } from "hono";
6-
import type { PartyServerOptions } from "partyserver";
6+
import type { Lobby, PartyServerOptions } from "partyserver";
7+
8+
/**
9+
* Extended options for the Hono middleware that pass the Hono context
10+
* to `onBeforeConnect` and `onBeforeRequest` as a third argument,
11+
* giving access to `c.env`, `c.var`, `c.get()`, etc.
12+
*/
13+
export type HonoPartyServerOptions<E extends Env> = Omit<
14+
PartyServerOptions,
15+
"onBeforeConnect" | "onBeforeRequest"
16+
> & {
17+
onBeforeConnect?: (
18+
req: Request,
19+
lobby: Lobby,
20+
c: Context<E>
21+
) => Response | Request | void | Promise<Response | Request | void>;
22+
onBeforeRequest?: (
23+
req: Request,
24+
lobby: Lobby,
25+
c: Context<E>
26+
) =>
27+
| Response
28+
| Request
29+
| void
30+
| Promise<Response | Request | undefined | void>;
31+
};
732

833
/**
934
* Configuration options for the PartyServer middleware
1035
*/
1136
type PartyServerMiddlewareContext<E extends Env> = {
1237
/** PartyServer-specific configuration options */
13-
options?: PartyServerOptions<E>;
38+
options?: HonoPartyServerOptions<E>;
1439
/** Optional error handler for caught errors */
1540
onError?: (error: Error) => void;
1641
};
@@ -22,12 +47,12 @@ type PartyServerMiddlewareContext<E extends Env> = {
2247
export function partyserverMiddleware<E extends Env = Env>(
2348
ctx?: PartyServerMiddlewareContext<E>
2449
) {
25-
return createMiddleware(async (c, next) => {
50+
return createMiddleware<E>(async (c, next) => {
2651
try {
27-
const handler = isWebSocketUpgrade(c)
28-
? handleWebSocketUpgrade
29-
: handleHttpRequest;
30-
const response = await handler(c, ctx?.options);
52+
const options = wrapOptionsWithContext(ctx?.options, c);
53+
const response = isWebSocketUpgrade(c)
54+
? await handleWebSocketUpgrade(c, options)
55+
: await handleHttpRequest(c, options);
3156

3257
return response === null ? await next() : response;
3358
} catch (error) {
@@ -40,6 +65,30 @@ export function partyserverMiddleware<E extends Env = Env>(
4065
});
4166
}
4267

68+
/**
69+
* Wraps the Hono-specific options into standard PartyServerOptions by
70+
* closing over the Hono context so callbacks receive it as a third arg.
71+
*/
72+
function wrapOptionsWithContext<E extends Env>(
73+
options: HonoPartyServerOptions<E> | undefined,
74+
c: Context<E>
75+
): PartyServerOptions | undefined {
76+
if (!options) return undefined;
77+
78+
const { onBeforeConnect, onBeforeRequest, ...rest } = options;
79+
return {
80+
...rest,
81+
...(onBeforeConnect && {
82+
onBeforeConnect: (req: Request, lobby: Lobby) =>
83+
onBeforeConnect(req, lobby, c)
84+
}),
85+
...(onBeforeRequest && {
86+
onBeforeRequest: (req: Request, lobby: Lobby) =>
87+
onBeforeRequest(req, lobby, c)
88+
})
89+
};
90+
}
91+
4392
/**
4493
* Checks if the incoming request is a WebSocket upgrade request
4594
* Looks for the 'upgrade' header with a value of 'websocket' (case-insensitive)
@@ -60,9 +109,9 @@ function createRequestFromContext(c: Context) {
60109
* Handles WebSocket upgrade requests
61110
* Returns a WebSocket upgrade response if successful, null otherwise
62111
*/
63-
async function handleWebSocketUpgrade<E extends Env>(
64-
c: Context<E>,
65-
options?: PartyServerOptions<E>
112+
async function handleWebSocketUpgrade(
113+
c: Context,
114+
options?: PartyServerOptions
66115
) {
67116
const req = createRequestFromContext(c);
68117
const response = await routePartykitRequest(req, env(c), options);
@@ -81,10 +130,7 @@ async function handleWebSocketUpgrade<E extends Env>(
81130
* Handles standard HTTP requests
82131
* Forwards the request to PartyServer and returns the response
83132
*/
84-
async function handleHttpRequest<E extends Env>(
85-
c: Context<E>,
86-
options?: PartyServerOptions<E>
87-
) {
133+
async function handleHttpRequest(c: Context, options?: PartyServerOptions) {
88134
const req = createRequestFromContext(c);
89135
return routePartykitRequest(req, env(c), options);
90136
}

0 commit comments

Comments
 (0)