diff --git a/packages/void/src/pages/protocol.ts b/packages/void/src/pages/protocol.ts index 7788ac7ba..d6d7eaed2 100644 --- a/packages/void/src/pages/protocol.ts +++ b/packages/void/src/pages/protocol.ts @@ -473,6 +473,8 @@ export async function handlePageGet( return pageJsonResponse(c, pageObj); } // Stream NDJSON: first line is PageObject, subsequent lines are deferred groups + c.header('X-VoidPages', 'true'); + c.header('Vary', 'X-VoidPages'); return streamDeferredResponse( JSON.stringify(pageObj) + '\n', deferredMap, diff --git a/packages/void/test/unit/pages-protocol.test.ts b/packages/void/test/unit/pages-protocol.test.ts index cc9d73462..87f198f03 100644 --- a/packages/void/test/unit/pages-protocol.test.ts +++ b/packages/void/test/unit/pages-protocol.test.ts @@ -794,6 +794,26 @@ describe('handlePageMutation', () => { }); describe('handlePageGet with deferred props', () => { + it('SPA — marks deferred NDJSON responses as Void Pages responses', async () => { + const c = await createTestContext('GET', '/dashboard', { + headers: { 'x-voidpages': 'true' }, + }); + + const response = await handlePageGet( + () => ({ + usage: defer(() => Promise.resolve({ requests: 100 })), + }), + undefined, + c, + 'dashboard', + mockRenderFn, + mockGetAssetTags, + ); + + expect(response.headers.get('X-VoidPages')).toBe('true'); + expect(response.headers.get('Vary')).toBe('X-VoidPages'); + }); + it('SPA — streams NDJSON with immediate props first, then deferred', async () => { const c = await createTestContext('GET', '/dashboard', { headers: { 'x-voidpages': 'true' },