diff --git a/packages/devframe/src/client/rpc.test.ts b/packages/devframe/src/client/rpc.test.ts index 118c97bc..3bd88f9d 100644 --- a/packages/devframe/src/client/rpc.test.ts +++ b/packages/devframe/src/client/rpc.test.ts @@ -56,7 +56,11 @@ describe('getDevframeRpcClient — connection meta base', () => { it('publishes the meta annotated with the absolute base it resolved from', async () => { const served: ConnectionMeta = { backend: 'websocket', websocket: { path: '__ws' } } - vi.stubGlobal('fetch', vi.fn(async () => ({ json: async () => served }) as any)) + vi.stubGlobal('fetch', vi.fn(async () => ({ + ok: true, + status: 200, + json: async () => served, + }) as any)) await getDevframeRpcClient({ baseURL: '/__foo/', otpParam: false }) @@ -66,6 +70,31 @@ describe('getDevframeRpcClient — connection meta base', () => { expect(lastWsUrl()).toBe('ws://localhost:5173/__foo/__ws') }) + it('falls back when a base returns a non-successful JSON response', async () => { + const served: ConnectionMeta = { backend: 'websocket', websocket: { path: '__ws' } } + const fetchSpy = vi.fn() + .mockResolvedValueOnce({ + ok: false, + status: 404, + json: async () => ({ status: 404, error: 'Not Found' }), + }) + .mockResolvedValueOnce({ + ok: true, + status: 200, + json: async () => served, + }) + vi.stubGlobal('fetch', fetchSpy) + + await getDevframeRpcClient({ + baseURL: ['/__missing/', '/__foo/'], + otpParam: false, + }) + + expect(fetchSpy).toHaveBeenNthCalledWith(1, '/__missing/__connection.json') + expect(fetchSpy).toHaveBeenNthCalledWith(2, '/__foo/__connection.json') + expect(lastWsUrl()).toBe('ws://localhost:5173/__foo/__ws') + }) + it('inherits the publisher base so a child at another base dials the shared endpoint', async () => { // A same-origin parent already published its meta, carrying the base it was // resolved against (`/__devtools/`), not this child's base (`/__foo/`). diff --git a/packages/devframe/src/client/rpc.ts b/packages/devframe/src/client/rpc.ts index 0704c60b..05a1cfac 100644 --- a/packages/devframe/src/client/rpc.ts +++ b/packages/devframe/src/client/rpc.ts @@ -305,8 +305,10 @@ export async function getDevframeRpcClient( const errors: Error[] = [] for (const base of bases) { try { - connectionMeta = await fetch(withBase(DEVFRAME_CONNECTION_META_FILENAME, base)) - .then(r => r.json()) as ConnectionMeta + const response = await fetch(withBase(DEVFRAME_CONNECTION_META_FILENAME, base)) + if (!response.ok) + throw new Error(`Failed to fetch connection meta: ${response.status}`) + connectionMeta = await response.json() as ConnectionMeta resolvedBaseURL = base // Publish the meta annotated with the absolute base it was resolved // against (`baseUrl`), so a same-origin child mounted at another base