From 05c13f0f69e5808b385b06401c8ad5823bf5dcee Mon Sep 17 00:00:00 2001 From: Hinata Masaki Date: Fri, 12 Sep 2025 17:44:39 +0900 Subject: [PATCH 1/4] update tsconfig --- tsconfig.base.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tsconfig.base.json b/tsconfig.base.json index 181eb9ba..4f647372 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -14,7 +14,8 @@ "target": "ES2021" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, "lib": [ "ES2022", - "DOM" + "DOM", + "DOM.Iterable" ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, "jsx": "react-jsx" /* Specify what JSX code is generated. */, // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ From 4f1f63d18912a801927a487c49d4ff35ea9617b6 Mon Sep 17 00:00:00 2001 From: Hinata Masaki Date: Fri, 12 Sep 2025 17:44:59 +0900 Subject: [PATCH 2/4] handle preflight requests for defined agent routes --- packages/agents/src/index.ts | 115 ++++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 42 deletions(-) diff --git a/packages/agents/src/index.ts b/packages/agents/src/index.ts index c48c1062..82169cd6 100644 --- a/packages/agents/src/index.ts +++ b/packages/agents/src/index.ts @@ -1577,65 +1577,96 @@ export type AgentContext = DurableObjectState; */ export type AgentOptions = PartyServerOptions & { /** - * Whether to enable CORS for the Agent + * Whether to enable CORS for the Agent. + * Implement `HeadersInit` to enable CORS and override the default CORS header fields. */ - cors?: boolean | HeadersInit | undefined; + cors?: boolean | HeadersInit; }; /** - * Route a request to the appropriate Agent + * Route a request to the appropriate Agent. + * + * An Agent is mapped to the route `/agents/[agent_namespace]/[agent_name]` (including sub-routes) for all methods, where: + * - `agent_namespace`: The kebab-case string of the Agent namespace (example: MyAgent maps to `my-agent`). + * - `agent_name`: The name of the Agent instance. + * + * If `options.cors` is `true` or satisfies `HeadersInit`, it will handle preflight requests to the agent route and set CORS header fields on all resolved responses. + * The header fields when `options.cors` is `true` are: + * - Access-Control-Allow-Credentials: true + * - Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS + * - Access-Control-Allow-Origin: * + * - Access-Control-Max-Age: 86400 + * * @param request Request to route * @param env Environment containing Agent bindings * @param options Routing options - * @returns Response from the Agent or undefined if no route matched + * @returns Response from the Agent or null if no route matched */ export async function routeAgentRequest( request: Request, env: Env, - options?: AgentOptions + options: AgentOptions = {} ) { - const corsHeaders = - options?.cors === true - ? { - "Access-Control-Allow-Credentials": "true", - "Access-Control-Allow-Methods": "GET, POST, HEAD, OPTIONS", - "Access-Control-Allow-Origin": "*", - "Access-Control-Max-Age": "86400" + let corsEnabled: boolean; + let corsHeaders: HeadersInit; + if (options.cors === true) { + corsEnabled = true; + corsHeaders = { + "Access-Control-Allow-Credentials": "true", + "Access-Control-Allow-Methods": "GET, POST, HEAD, OPTIONS", + "Access-Control-Allow-Origin": "*", + "Access-Control-Max-Age": "86400" + }; + } else if (typeof options.cors === "object" || Array.isArray(options.cors)) { + // options.cors satisfies HeadersInit. + + corsEnabled = true; + corsHeaders = options.cors; + } else { + corsEnabled = false; + corsHeaders = {}; + } + + let response = await routePartykitRequest(request, env as any, { + prefix: "agents", + jurisdiction: options.jurisdiction, + locationHint: options.locationHint, + // Preflight request with `Upgrade` header field don't exist. + onBeforeConnect: options.onBeforeConnect as any, + onBeforeRequest: async (req, lobby) => { + if (options.onBeforeRequest !== undefined) { + const reqOrRes = await options.onBeforeRequest(req, lobby as any); + if (reqOrRes instanceof Response) { + return reqOrRes; } - : options?.cors; + if (reqOrRes instanceof Request) { + req = reqOrRes; + } + } - if (request.method === "OPTIONS") { - if (corsHeaders) { - return new Response(null, { - headers: corsHeaders - }); - } - console.warn( - "Received an OPTIONS request, but cors was not enabled. Pass `cors: true` or `cors: { ...custom cors headers }` to routeAgentRequest to enable CORS." - ); + if (req.method === "OPTIONS") { + if (corsEnabled) { + return new Response(null, { + headers: corsHeaders + }); + } + console.warn( + "Received an OPTIONS request, but cors was not enabled. Pass `cors: true` or `cors: { ...custom cors headers }` to routeAgentRequest to enable CORS." + ); + } + }, + props: options.props + }); + + if (response === null) { + return null; } - let response = await routePartykitRequest( - request, - env as Record, - { - prefix: "agents", - ...(options as PartyServerOptions>) + if (request.headers.get("Upgrade")?.toLowerCase() !== "websocket") { + const headersEntries = new Headers(corsHeaders).entries(); + for (const [fieldName, fieldValue] of headersEntries) { + response.headers.set(fieldName, fieldValue); } - ); - - if ( - response && - corsHeaders && - request.headers.get("upgrade")?.toLowerCase() !== "websocket" && - request.headers.get("Upgrade")?.toLowerCase() !== "websocket" - ) { - response = new Response(response.body, { - headers: { - ...response.headers, - ...corsHeaders - } - }); } return response; } From 496b9045baf91e570972b1d5ec216e9526cc152d Mon Sep 17 00:00:00 2001 From: Hinata Masaki Date: Fri, 12 Sep 2025 17:45:05 +0900 Subject: [PATCH 3/4] add changeset --- .changeset/good-camels-matter.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/good-camels-matter.md diff --git a/.changeset/good-camels-matter.md b/.changeset/good-camels-matter.md new file mode 100644 index 00000000..d5e91792 --- /dev/null +++ b/.changeset/good-camels-matter.md @@ -0,0 +1,5 @@ +--- +"agents": patch +--- + +Update `routeAgentRequest()` to handle preflight requests only for defined routes. From 7cc567eaf0058c6ee1aa7e7ec7400410109f47a1 Mon Sep 17 00:00:00 2001 From: Hinata Masaki Date: Tue, 16 Sep 2025 10:23:56 +0900 Subject: [PATCH 4/4] fix eslint errors --- packages/agents/src/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/agents/src/index.ts b/packages/agents/src/index.ts index 82169cd6..89e37825 100644 --- a/packages/agents/src/index.ts +++ b/packages/agents/src/index.ts @@ -1627,24 +1627,25 @@ export async function routeAgentRequest( corsHeaders = {}; } - let response = await routePartykitRequest(request, env as any, { + const response = await routePartykitRequest(request, env as any, { prefix: "agents", jurisdiction: options.jurisdiction, locationHint: options.locationHint, // Preflight request with `Upgrade` header field don't exist. onBeforeConnect: options.onBeforeConnect as any, onBeforeRequest: async (req, lobby) => { + let resolvedRequest = req; if (options.onBeforeRequest !== undefined) { const reqOrRes = await options.onBeforeRequest(req, lobby as any); if (reqOrRes instanceof Response) { return reqOrRes; } if (reqOrRes instanceof Request) { - req = reqOrRes; + resolvedRequest = reqOrRes; } } - if (req.method === "OPTIONS") { + if (resolvedRequest.method === "OPTIONS") { if (corsEnabled) { return new Response(null, { headers: corsHeaders