From 0745d494ab4fa8d242531df5d2f67b10370864d0 Mon Sep 17 00:00:00 2001 From: Jacob Zweifel Date: Thu, 27 Aug 2026 21:41:43 -0400 Subject: [PATCH] Call the metrics fetch with globalThis, not the config object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit api.fetch(...) is a method call, so fetch received the config object as its `this` — workerd rejects fetch invoked with a foreign receiver (Illegal invocation), which broke every /admin/analytics query in production. Node's fetch tolerates it, so tests never saw it; a new test pins the receiver. Co-Authored-By: Claude --- apps/web/src/lib/server/metrics/metrics.test.ts | 15 +++++++++++++++ apps/web/src/lib/server/metrics/query.ts | 4 +++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/apps/web/src/lib/server/metrics/metrics.test.ts b/apps/web/src/lib/server/metrics/metrics.test.ts index 3fa6005..cfa9cb6 100644 --- a/apps/web/src/lib/server/metrics/metrics.test.ts +++ b/apps/web/src/lib/server/metrics/metrics.test.ts @@ -302,6 +302,21 @@ describe('siteMetrics', () => { expect(empty.byDay).toEqual([]); }); + it('never calls fetch with the api object as `this` — workerd rejects that', async () => { + const thisValues: unknown[] = []; + const api = { + accountId: 'acct', + token: 'tok', + fetch: function (this: unknown) { + thisValues.push(this); + return Promise.resolve(new Response(JSON.stringify({ data: [] }))); + } as typeof fetch + }; + await siteMetrics(api, DID, 7); + expect(thisValues.length).toBeGreaterThan(0); + for (const t of thisValues) expect(t).toBe(globalThis); + }); + it('surfaces an API failure with the status', async () => { const api = { accountId: 'acct', diff --git a/apps/web/src/lib/server/metrics/query.ts b/apps/web/src/lib/server/metrics/query.ts index 5b022ef..d1b68ff 100644 --- a/apps/web/src/lib/server/metrics/query.ts +++ b/apps/web/src/lib/server/metrics/query.ts @@ -49,7 +49,9 @@ export function didLiteral(did: string): string { } async function sql(api: MetricsApi, query: string): Promise[]> { - const res = await api.fetch( + // fetch rejects a foreign `this` (the api object a method call would pass). + const res = await api.fetch.call( + globalThis, `https://api.cloudflare.com/client/v4/accounts/${api.accountId}/analytics_engine/sql`, { method: 'POST', -- 2.51.2