Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/short-oranges-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sveltejs/kit": minor
---

feat: inline load fetch `response.body` stream data as base64 in page
36 changes: 36 additions & 0 deletions packages/kit/src/runtime/server/page/load_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,9 @@ export function create_universal_fetch(event, state, fetched, csr, resolve_opts)
}
}

/** @type {ReadableStream<Uint8Array>} */
let teed_body;

const proxy = new Proxy(response, {
get(response, key, _receiver) {
/**
Expand Down Expand Up @@ -342,6 +345,39 @@ export function create_universal_fetch(event, state, fetched, csr, resolve_opts)
});
}

if (key === 'body') {
if (response.body === null) {
return null;
}

if (teed_body) {
return teed_body;
}

const [a, b] = response.body.tee();

void (async () => {
let result = new Uint8Array();

for await (const chunk of a) {
const combined = new Uint8Array(result.length + chunk.length);

combined.set(result, 0);
combined.set(chunk, result.length);

result = combined;
}

if (dependency) {
dependency.body = new Uint8Array(result);
}

void push_fetched(base64_encode(result), true);
})();

return (teed_body = b);
}

if (key === 'arrayBuffer') {
return async () => {
const buffer = await response.arrayBuffer();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const body_stream_to_buffer = async (body) => {
let buffer = new Uint8Array();
const reader = body.getReader();
while (true) {
const { done, value } = await reader.read();
if (value != null) {
const newBuffer = new Uint8Array(buffer.length + value.length);
newBuffer.set(buffer, 0);
newBuffer.set(value, buffer.length);
buffer = newBuffer;
}
if (done) break;
}
return buffer;
};

export async function load({ fetch }) {
const res = await fetch('/load/fetch-body-stream-b64/data');

const l = await fetch('/load/fetch-body-stream-b64/data', {
body: Uint8Array.from(Array(256).fill(0), (_, i) => i),
method: 'POST'
});

return {
data: await body_stream_to_buffer(res.body),
data_long: await body_stream_to_buffer(l.body)
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script>
export let data;

$: arr = [...new Uint8Array(data.data)];

let ok = 'Ok';

$: {
const p = new Uint8Array(data.data_long);
ok = p.length === 256 ? 'Ok' : 'Wrong length';

if (p.length === 256) {
for (let i = 0; i < p.length; i++) {
if (p[i] !== i) {
ok = `Expected ${i} but got ${p[i]}`;
break;
}
}
}
}
</script>

<span class="test-content">{JSON.stringify(arr)}</span>

<br />

{ok}
<span style="word-wrap: break-word;">
{JSON.stringify([...new Uint8Array(data.data_long)])}
</span>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const GET = () => {
return new Response(new Uint8Array([1, 2, 3, 4]));
};

export const POST = async ({ request }) => {
return new Response(await request.arrayBuffer());
};
22 changes: 22 additions & 0 deletions packages/kit/test/apps/basics/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,28 @@ test.describe('Load', () => {
}
});

test('fetches using a body stream serialized with b64', async ({ page, javaScriptEnabled }) => {
await page.goto('/load/fetch-body-stream-b64');

expect(await page.textContent('.test-content')).toBe('[1,2,3,4]');

if (!javaScriptEnabled) {
const payload = '{"status":200,"statusText":"","headers":{},"body":"AQIDBA=="}';
const post_payload =
'{"status":200,"statusText":"","headers":{},"body":"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=="}';

const script_content = await page.innerHTML(
'script[data-sveltekit-fetched][data-b64][data-url="/load/fetch-body-stream-b64/data"]'
);
const post_script_content = await page.innerHTML(
'script[data-sveltekit-fetched][data-b64][data-url="/load/fetch-body-stream-b64/data"][data-hash="16h3sp1"]'
);

expect(script_content).toBe(payload);
expect(post_script_content).toBe(post_payload);
}
});

test('json string is returned', async ({ page }) => {
await page.goto('/load/relay');
expect(await page.textContent('h1')).toBe('42');
Expand Down
Loading