-
Notifications
You must be signed in to change notification settings - Fork 11
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
stream ReadableStream
instances
#51
base: main
Are you sure you want to change the base?
Conversation
@@ -238,22 +271,24 @@ export function encode( | |||
if (Array.isArray(id)) { | |||
controller.enqueue( | |||
textEncoder.encode( | |||
`${TYPE_ERROR}${deferredId}:[["${TYPE_PREVIOUS_RESOLVED}",${id[0]}]]\n` | |||
`${TYPE_PROMISE}${TYPE_ERROR}${deferredId}:[["${TYPE_PREVIOUS_RESOLVED}",${id[0]}]]\n` |
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.
I did change the existing transport format a bit here - this is not strictly necessary, but this way, we have less of an explosion of types & type cases, and a bit more consistency.
It went from
E5
to PE5
to signal that P
romise 5 E
rrored.
Further down I introduced
TE5
to signal that sT
ream 5 E
rrored.
This "combination of characters" is kinda necessary as otherwise I would have to introduce another new signal for "stream done" as well as "stream errored" and re-use a few characters in ways that do not necessarily make sense
So instead, further down I introduce
T5
for "new value in sT
ream 5"TO5
for "sT
ream 5 is dO
ne"TE5
for "sT
ream 5E
rrored"
if (done) { | ||
controller.enqueue( | ||
textEncoder.encode( | ||
`${TYPE_STREAM}${TYPE_DONE}${streamId}:[]\n` |
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.
`${TYPE_STREAM}${TYPE_DONE}${streamId}:[]\n` | |
`${TYPE_STREAM}${TYPE_DONE}${streamId}\n` |
This could be modified like this to save a few characters, but it seemed more consistent that way - I'm very open for input there :)
const [left, right] = input.tee(); | ||
input.getReader = left.getReader.bind(left); | ||
input.cancel = left.cancel.bind(left); | ||
input.pipeThrough = left.pipeThrough.bind(left); | ||
input.pipeTo = left.pipeTo.bind(left); | ||
input.tee = left.tee.bind(left); | ||
Object.defineProperty(input, "locked", { | ||
get: () => left.locked, | ||
}); | ||
streams[index] = right; |
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.
This could also be omitted if there were a consensus that encoding is a destructive action - React does so - but I honestly felt a bit more comfortable with this tee
approach.
const [left, right] = input.tee(); | |
input.getReader = left.getReader.bind(left); | |
input.cancel = left.cancel.bind(left); | |
input.pipeThrough = left.pipeThrough.bind(left); | |
input.pipeTo = left.pipeTo.bind(left); | |
input.tee = left.tee.bind(left); | |
Object.defineProperty(input, "locked", { | |
get: () => left.locked, | |
}); | |
streams[index] = right; | |
streams[index] = input; |
const inputValue = await inputReader.read(); | ||
const decodedValue = await decodedReader.read(); |
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.
These tests currently test the "non-destructive" approach with tee
, which makes it possible to still consume the original input stream.
@@ -88,8 +91,10 @@ async function decodeDeferred( | |||
const line = read.value; | |||
switch (line[0]) { | |||
case TYPE_PROMISE: { | |||
const isError = line[1] === TYPE_ERROR; |
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.
The case TYPE_ERROR:
was extremely similar, and falls into the TYPE_PROMISE
case now that I slightly changed the format, so I could delete that second case
with a few minor adjustments.
await decoded.done; | ||
}); | ||
|
||
test("streams in a stream", async () => { |
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.
Yes, this works now 🤣
Okay, I think I'm happy with this. Ready for review/discussion :) |
I would love to use this in Remix to use streaming loaders and actions, my use case is running a very slow loader while showing feedback to the user (like percentage completed) |
This allows for
ReadableStream
instances to be passed through the stream.