-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: inline fetch response.body
data to page
#11473
base: main
Are you sure you want to change the base?
Conversation
🦋 Changeset detectedLatest commit: 6a0a580 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
if (key === 'body') { | ||
const body = response.body; | ||
if (!body) return body; | ||
const [a, b] = body.tee(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any concerns about memory? response data will be copied to two locations in memory if I understand this right.
} | ||
} | ||
reader.read().then(buffer_to_fetched); | ||
return b; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a
is guaranteed to close (done === true
) before b
. So there should not be any race conditions or deadlocks.
Unless someone queries in SSR a long running endpoint which doesn't close. Most people should know not to do that. Let me know if you think we should try to add a guard against that.
I edited the PR description to put the meaty stuff above the boilerplate. Letting you know as sometimes when it's below we don't see it and the PR ends up sitting longer because no one has context around it |
Thank you — on the whole this seems reasonable (the memory thing does make me a tiny bit nervous but people really shouldn't be making giant requests in this context so in practice it's probably fine). One wrinkle: I think const response = await fetch(url);
const body = response.body; // causes the body to be cloned, and reading to start
await Promise.resolve();
console.log(response.bodyUsed); // true, even though we haven't touched `body` yet! Any smart ideas on how to address that? |
@Rich-Harris Good catch. I spent a bit of time looking for clever, idiomatic ways using the fetch and stream APIs, but I could not find a way to keep
What does seem to work is delaying this same previous Here is what seems to work, it requires adding another proxy, this time around if (key === 'body') {
const body = response.body;
if (!body) return body;
/**
* @param {ReadableStream} body
*/
function tee_and_buffer_body(body) {
const [a, b] = body.tee();
let buffer = new Uint8Array();
const reader = a.getReader();
/**
* @param {{
* done: boolean
* value?: Uint8Array
* }} opts
*/
function buffer_to_fetched({ done, value }) {
if (done) {
if (dependency) {
dependency.body = new Uint8Array(buffer);
}
push_fetched(b64_encode(buffer), true);
} else if (value) {
const newBuffer = new Uint8Array(buffer.length + value.length);
newBuffer.set(buffer, 0);
newBuffer.set(value, buffer.length);
buffer = newBuffer;
reader.read().then(buffer_to_fetched);
}
}
reader.read().then(buffer_to_fetched);
return b;
}
/** @type {ReadableStream} */
let teedBody;
return new Proxy(body, {
get(body, key, _receiver) {
if (
key === 'getReader' ||
key === 'pipeThrough' ||
key === 'pipeTo' ||
key === 'tee'
) {
teedBody = teedBody || tee_and_buffer_body(body);
return teedBody[key].bind(teedBody);
}
}
});
} This exercise also made me realize a second problem with this first implementation, calling It's not much prettier in terms of code, but I think still acceptable. You tell me. |
we-have-to-go-deeper-jpg Unfortunately, this still doesn't quite solve it, because you could do It's going to end up being a game of whack-a-mole and I don't know that it's worth it to be honest. It feels like enough of an edge case that it's probably fine? (Famous last words.) Handling the double |
@Rich-Harris You're right, it doesn't quite solve it, I should have noticed. re: double |
2e7976f
to
c1069c4
Compare
c1069c4
to
be1b7bf
Compare
be1b7bf
to
4f70e28
Compare
response.body
data to pageresponse.body
data to page
I've marked this as draft for now, but feel free to mark it as ready when you'd like someone to review it. |
thanks @eltigerchino , this is actually ready for review, I guess I didn't make that clear in my last message. When I say "it doesn't quite solve it", it relate to the issue of |
4f70e28
to
350da95
Compare
d6d38d7
to
5ee975e
Compare
5ee975e
to
d26584d
Compare
d26584d
to
ad34198
Compare
069ee63
to
b3e9941
Compare
b3e9941
to
87a7121
Compare
87a7121
to
6a0a580
Compare
preview: https://svelte-dev-git-preview-kit-11473-svelte.vercel.app/ this is an automated message |
The general topic is about widening the support of kit's magic
fetch
in load functions. Currently most response accessors are supported by kit (.json()
,.text()
,.arrayBuffer()
), this PR is about the.body
response accessor.This PR follows up from recent PR #10535
The previous PR linked above added support for inlining fetch
response.arrayBuffer()
data on the page.This PR adds support for inlining fetch
response.body.getReader()
data on the page.Much of the groundwork of using base64 was laid by @Elia872 in his previous PR.
My app's API client library handles both long running streams (such as messages in a chat widget) and standard run-of-the-mill api calls via a single call to
response.body.getReader()
. It's a relatively popular library in the small world of GRPC clients, you can see it here.Long running streams shouldn't be invoked in SSR and this PR doesn't support that. But any short lived streamed data stream fetched via
.body
could also be inlined in the page, much like is already done for the otherresponse.arrrayBuffer/text/json/...()
methods.Pros:
Cons:
.json()
, some.text()
, few.arrayBuffer()
and scant.body
I skipped creating an issue because I already have this code lying around, I'm using it today in my app.
Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
Tests
pnpm test
and lint the project withpnpm lint
andpnpm check
Changesets
pnpm changeset
and following the prompts. Changesets that add features should beminor
and those that fix bugs should bepatch
. Please prefix changeset messages withfeat:
,fix:
, orchore:
.