From f2bd3e38e22410f4360ea9cdfe43dcbdb11b5fdb Mon Sep 17 00:00:00 2001 From: Eric Meehan Date: Tue, 20 Oct 2026 10:12:33 -0400 Subject: [PATCH] RPC --- shared/rpc-client.ts | 5 ++++- tests/rpc-client.test.ts | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/shared/rpc-client.ts b/shared/rpc-client.ts index 44126ef..428a757 100755 --- a/shared/rpc-client.ts +++ b/shared/rpc-client.ts @@ -9,7 +9,10 @@ export class StateClient { private cache: { chainId: string; blocks: TestnetBlock[]; bytes: number } | undefined; private inFlight: Promise | undefined; private readonly fetcher: typeof fetch; - constructor(fetcher: typeof fetch = fetch) { this.fetcher = fetcher; } + constructor(fetcher: typeof fetch = globalThis.fetch) { + // Browser Web APIs require the Window/global receiver, not this StateClient instance. + this.fetcher = fetcher.bind(globalThis); + } async json(path: string, signal: AbortSignal): Promise { const response = await this.fetcher(`/api/${path}`, { cache: 'no-store', redirect: 'error', signal }); if (!/^application\/json(?:\s*;|$)/i.test(response.headers.get('content-type') ?? '') || !response.body) throw new Error('Node must return JSON.'); diff --git a/tests/rpc-client.test.ts b/tests/rpc-client.test.ts index bfe2631..4789d34 100755 --- a/tests/rpc-client.test.ts +++ b/tests/rpc-client.test.ts @@ -6,6 +6,18 @@ import { NETWORK, type TestnetBlock } from '../shared/testnet.ts'; const genesis='0'.repeat(64),chainId='f'.repeat(64); const block=(height:number,previousHash:string):TestnetBlock=>({ network:NETWORK,chainId,height, hash:height.toString(16).padStart(64,'0'),previousHash,transactions:[] }) as unknown as TestnetBlock; +test('default browser-style fetch receives the global receiver, not StateClient',async()=>{ + const original=globalThis.fetch; + let calls=0; + globalThis.fetch=async function(this:unknown,input:RequestInfo|URL,init?:RequestInit){ + assert.equal(this,globalThis,'native browser fetch would throw Illegal invocation for another receiver'); + assert.equal(input,'/api/summary');assert.equal(init?.redirect,'error');calls++; + return Response.json({network:NETWORK,chainId,genesis,tip:genesis,height:0,revision:0, + cumulativeWork:'0',pendingTransfers:[],peers:0,algorithm:'RandomX',maturity:2}); + }; + try{const state=await new StateClient().load();assert.equal(state.tip,genesis);assert.equal(calls,2);} + finally{globalThis.fetch=original;} +}); function fixture(count:number){ const blocks:TestnetBlock[]=[];for(let i=0;i