Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,24 @@ function injectMetaTagsInResponse(originalResponse: Response): Response {
}
}

// Once we've injected, pass later chunks through untouched so a <head> inside a
// quoted attribute in a later chunk isn't matched again.
let injected = false;

let errored = false;
try {
for await (const chunk of bodyReporter()) {
const html = typeof chunk === 'string' ? chunk : decoder.decode(chunk, { stream: true });

if (injected) {
controller.enqueue(new TextEncoder().encode(html));
continue;
}

const modifiedHtml = addMetaTagToHead(html, metaTagsStr);
if (modifiedHtml !== html) {
injected = true;
}
controller.enqueue(new TextEncoder().encode(modifiedHtml));
}
} catch (e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,31 @@ describe('wrapFetchWithSentry', () => {
expect(html).toContain('data-content="<head>ignore"');
});

it('injects meta tags only once when a later chunk has <head> inside a quoted attribute', async () => {
const encoder = new TextEncoder();
const chunks = ['<head></head><body><div data-content="junk', '<head>junk"></div></body>'];
const body = new ReadableStream({
start(controller) {
for (const c of chunks) controller.enqueue(encoder.encode(c));
controller.close();
},
});
const mockResponse = new Response(body, {
headers: new Headers({ 'content-type': 'text/html' }),
});
const fetchFn = vi.fn().mockResolvedValue(mockResponse);

const serverEntry = wrapFetchWithSentry({ fetch: fetchFn });
const request = new Request('http://localhost:3000/');

const response = await serverEntry.fetch(request);
const html = await response.text();

expect(html.match(/name="sentry-trace"/g)).toHaveLength(1);
expect(html).toContain('<head><meta name="sentry-trace"');
expect(html).toContain('data-content="junk<head>junk"');
});

it('captures exception when HTML response body stream errors', async () => {
const streamError = new Error('stream read error');
const body = new ReadableStream({
Expand Down
Loading