Skip to content
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(response): add sendIterable util #655

Merged
merged 8 commits into from
Feb 24, 2024

Conversation

passionate-bram
Copy link
Contributor

πŸ”— Linked issue

#581

❓ Type of change

  • πŸ“– Documentation (updates to the documentation, readme, or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • πŸ‘Œ Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

πŸ“š Description

Introduces a new utility function sendIterable, relevant types and an additional helper function are also exposed.

Newly exposed API:

export function sendIterable<Value = unknown, Return = unknown>(
  event: H3Event,
  iterable: IterationSource<Value, Return>,
  serializer: IteratorSerializer<Value | Return> = serializeIterableValue
) : Promise<void> {/* snip */}

// Supported iterable and iterator types of `sendIterable`
export type IterationSource<Val, Ret = Val> =
  | Iterable<Val>
  | AsyncIterable<Val>
  | Iterator<Val, Ret | undefined>
  | AsyncIterator<Val, Ret | undefined>
  | (() => (
    | Iterator<Val, Ret | undefined>
    | AsyncIterator<Val, Ret | undefined>
  ));

// The response stream (event.node.res) is in byte-mode, so you the types of data that can be written is restricted.
type SendableValue = string | Buffer | Uint8Array;
// The `undefined` return value is a short-cut to not writing anything to the response stream, for example if the data type cannot be serialized.
export type IteratorSerializer<Value> = (value: Value) => SendableValue | undefined;

// Default serializer that turns types that are not directly writable to the underlying network stream into ones that are.
export function serializeIterableValue(value: unknown): SendableValue | undefined {/* snip */}

The new feature allows users of h3 (and by extension Nitro and Nuxt) to write generator functions (function*(){}) that produce a response piece-by-piece. For more reason on justification see the linked issue.

Resolves #581

πŸ“ Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@pi0 pi0 changed the title Feat/send iterable feat(response): add sendIterable util Feb 24, 2024
Copy link
Member

@pi0 pi0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Copy link

codecov bot commented Feb 24, 2024

Codecov Report

Attention: Patch coverage is 97.01493% with 4 lines in your changes are missing coverage. Please review.

Project coverage is 78.14%. Comparing base (a58d7c9) to head (62c3030).
Report is 19 commits behind head on main.

Files Patch % Lines
src/utils/internal/iteratable.ts 96.87% 2 Missing ⚠️
src/utils/response.ts 97.14% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #655      +/-   ##
==========================================
+ Coverage   77.83%   78.14%   +0.31%     
==========================================
  Files          47       53       +6     
  Lines        4286     4970     +684     
  Branches      611      661      +50     
==========================================
+ Hits         3336     3884     +548     
- Misses        933     1067     +134     
- Partials       17       19       +2     

β˜” View full report in Codecov by Sentry.
πŸ“’ Have feedback on the report? Share it here.

@pi0 pi0 merged commit cc80a2d into unjs:main Feb 24, 2024
6 checks passed
@passionate-bram
Copy link
Contributor Author

@pi0 A note worth mentioning in the docs is that the default buffering for server responses is per 16k bytes. So users may be surprised that the first yield writes out to the stream but subsequent ones do not, until they exceed the 16k buffer at which point all data up to that point is written out.

This is a bit unfortunate for the primary use case to have live output for a long-running process, but by mentioning it users can either work around it (by somehow creating a different buffer for the node server) or accepting the limitation.

See also unjs/listhen#161

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support for AsyncGenerator event handlers, for streaming long running responses
2 participants