Skip to content

Commit 0e0e88f

Browse files
committed
fix: limit browser route cache sniffing
Only parse cloned JSON responses for browser metadata endpoints so unrelated API calls don't pay the route cache warm-up cost, and cover the regression with a focused routing fetch test. Made-with: Cursor
1 parent 9b24280 commit 0e0e88f

2 files changed

Lines changed: 41 additions & 2 deletions

File tree

src/lib/browser-routing.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export class BrowserRouteCache {
2929

3030
const BROWSER_ROUTING_SUBRESOURCES_ENV = 'KERNEL_BROWSER_ROUTING_SUBRESOURCES';
3131
const DEFAULT_BROWSER_ROUTING_SUBRESOURCES = ['curl'];
32+
const BROWSER_ROUTE_CACHEABLE_PATH = /^\/(?:v\d+\/)?browsers(?:\/[^/]+)?\/?$/;
3233

3334
export function browserRoutingSubresourcesFromEnv(): string[] {
3435
const raw = readBrowserRoutingSubresourcesEnv();
@@ -63,12 +64,20 @@ export function createRoutingFetch(
6364

6465
return async (input, init) => {
6566
const request = new Request(input, init);
67+
const shouldSniff = shouldSniffAndPopulateCache(request, apiOrigin);
6668
const response = await routeRequest(innerFetch, { input, init, request }, apiOrigin, allowed, cache);
67-
await sniffAndPopulateCache(response, cache);
69+
if (shouldSniff) {
70+
await sniffAndPopulateCache(response, cache);
71+
}
6872
return response;
6973
};
7074
}
7175

76+
function shouldSniffAndPopulateCache(request: Request, apiOrigin: string): boolean {
77+
const url = new URL(request.url);
78+
return url.origin === apiOrigin && BROWSER_ROUTE_CACHEABLE_PATH.test(url.pathname);
79+
}
80+
7281
function browserRouteFromValue(value: unknown): BrowserRoute | undefined {
7382
if (!value || typeof value !== 'object') {
7483
return undefined;

tests/lib/browser-routing.test.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import Kernel from '@onkernel/sdk';
22

3-
import { browserRoutingSubresourcesFromEnv } from '../../src/lib/browser-routing';
3+
import {
4+
BrowserRouteCache,
5+
browserRoutingSubresourcesFromEnv,
6+
createRoutingFetch,
7+
} from '../../src/lib/browser-routing';
48

59
describe('browser routing', () => {
610
const browserRoutingEnv = 'KERNEL_BROWSER_ROUTING_SUBRESOURCES';
@@ -127,6 +131,32 @@ describe('browser routing', () => {
127131
});
128132
});
129133

134+
test('skips cache sniffing for non-browser JSON responses', async () => {
135+
let cloneCalled = false;
136+
const wrappedFetch = createRoutingFetch(
137+
async () => {
138+
const response = Response.json({ ok: true });
139+
const clone = response.clone.bind(response);
140+
Object.defineProperty(response, 'clone', {
141+
value: () => {
142+
cloneCalled = true;
143+
return clone();
144+
},
145+
});
146+
return response;
147+
},
148+
{
149+
apiBaseURL: 'https://api.example/',
150+
subresources: ['process'],
151+
cache: new BrowserRouteCache(),
152+
},
153+
);
154+
155+
await wrappedFetch('https://api.example/deployments');
156+
157+
expect(cloneCalled).toBe(false);
158+
});
159+
130160
test('preserves custom fetch options for both API and routed VM requests', async () => {
131161
await withBrowserRoutingEnv('process', async () => {
132162
const dispatcher = Symbol('dispatcher');

0 commit comments

Comments
 (0)