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

Fix streaming splits in realtime streams v2 #1549

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

ericallam
Copy link
Member

@ericallam ericallam commented Dec 11, 2024

Summary by CodeRabbit

  • New Features

    • Introduced a new LineTransformStream class for improved line-by-line processing of incoming data.
    • Enhanced the ElectricStreamSubscription class with better logging and error handling capabilities.
    • Added a new script for deployment in the project.
  • Bug Fixes

    • Addressed issues with real-time streaming splits in the @trigger.dev/sdk package.
  • Improvements

    • Simplified data ingestion logic for individual entries in the database.
    • Updated processing methods to enhance performance and reliability of streaming operations.
    • Enhanced logging for streaming tasks to improve debugging capabilities.
    • Implemented stricter API access rules based on environment variables.

Copy link

changeset-bot bot commented Dec 11, 2024

🦋 Changeset detected

Latest commit: d4f533f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 11 packages
Name Type
@trigger.dev/sdk Patch
@trigger.dev/build Patch
@trigger.dev/core Patch
@trigger.dev/react-hooks Patch
@trigger.dev/rsc Patch
@trigger.dev/database Patch
@trigger.dev/otlp-importer Patch
trigger.dev Patch
@internal/redis-worker Patch
@internal/zod-worker Patch
@internal/testcontainers Patch

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

Copy link
Contributor

coderabbitai bot commented Dec 11, 2024

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 eslint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/webapp/server.ts

Oops! Something went wrong! :(

ESLint: 8.45.0

ESLint couldn't find the config "custom" to extend from. Please check that the name of the config is correct.

The config "custom" was referenced from the config file in "/.eslintrc.js".

If you still have problems, please stop by https://eslint.org/chat/help to chat with the team.

Walkthrough

The pull request introduces a patch for the @trigger.dev/sdk package to address issues with real-time streaming splits. It modifies the DatabaseRealtimeStreams class to simplify the data ingestion logic, switching from batch processing to individual entry creation in the database. Additionally, it enhances the ElectricStreamSubscription class by adding logging and a new LineTransformStream for line-by-line processing of incoming data. A new LineTransformStream class is also introduced to manage string chunk processing, improving the overall functionality and reliability of the streaming operations.

Changes

File Path Change Summary
.changeset/rude-walls-help.md Patch applied for @trigger.dev/sdk to address real-time streaming splits issues.
apps/webapp/app/services/realtime/databaseRealtimeStreams.server.ts Modified ingestData method to simplify data ingestion, switching from createMany to create for individual entries.
packages/core/src/v3/apiClient/runStream.ts Added LineTransformStream import; updated subscribe method to include logging and line-by-line processing.
packages/core/src/v3/apiClient/stream.ts Introduced new class LineTransformStream for processing string chunks into lines.
apps/webapp/test/realtimeClient.test.ts Added conditional skip to the describe block to prevent tests from running in GitHub Actions environment.
apps/webapp/server.ts Introduced middleware to restrict access based on ALLOW_ONLY_REALTIME_API environment variable.
apps/webapp/test/authorizationRateLimitMiddleware.test.ts Added conditional skip to the describe block for GitHub Actions environment.
packages/core/src/v3/runMetadata/manager.ts Updated StandardMetadataManager to include streamsBaseUrl and modified stream method parameters.
packages/core/src/v3/runMetadata/metadataStream.ts Changed MetadataOptions to use source instead of iterator, updating internal stream handling accordingly.
references/nextjs-realtime/package.json Added new script "deploy": "trigger deploy" to package.json.
references/nextjs-realtime/src/components/TriggerButtonWithStreaming.tsx Removed experimental_throttleInMs property from useRealtimeTaskTriggerWithStreams hook.
references/nextjs-realtime/src/trigger/ai.ts Removed streaming of textStream from openaiStreaming schema task and enhanced logging in openaiSDKStreaming.

Possibly related PRs

Suggested reviewers

  • matt-aitken

🐇 In the streams where data flows,
A patch was made, and now it glows.
With lines transformed and entries neat,
Each chunk is logged, a tasty treat!
Real-time magic, swift and bright,
In our code, it feels just right! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (3)
apps/webapp/app/services/realtime/databaseRealtimeStreams.server.ts (1)

57-63: Consider batch processing for better performance

The switch from batch processing to individual inserts might impact performance when dealing with high-volume streams. Consider using a buffering strategy to batch inserts while maintaining real-time characteristics.

-await this.options.prisma.realtimeStreamChunk.create({
-  data: {
-    runId,
-    key: streamId,
-    sequence: sequence++,
-    value,
-  },
-});
+const BATCH_SIZE = 100;
+let buffer = [];
+buffer.push({
+  runId,
+  key: streamId,
+  sequence: sequence++,
+  value,
+});
+
+if (buffer.length >= BATCH_SIZE) {
+  await this.options.prisma.realtimeStreamChunk.createMany({
+    data: buffer,
+  });
+  buffer = [];
+}
packages/core/src/v3/apiClient/stream.ts (1)

225-231: Consider reducing debug logging in production

The extensive console logging might impact performance in production. Consider using a debug flag or environment variable to control logging.

-console.log("LineTransformStream", {
-  chunk,
-  lines,
-  fullLines,
-  buffer: this.buffer,
-  streamId,
-});
+if (process.env.DEBUG_STREAMS === 'true') {
+  console.log("LineTransformStream", {
+    chunk,
+    lines,
+    fullLines,
+    buffer: this.buffer,
+    streamId,
+  });
+}
packages/core/src/v3/apiClient/runStream.ts (1)

217-238: Consider optimizing the transform pipeline

The current implementation has multiple transform stages with logging, which might impact performance. Consider:

  1. Combining transforms where possible
  2. Making logging conditional
 return zodShapeStream(SubscribeRealtimeStreamChunkRawShape, this.url, this.options)
   .pipeThrough(
     new TransformStream({
       transform(chunk, controller) {
-        console.log("ElectricStreamSubscription chunk.value", chunk.value);
+        if (process.env.DEBUG_STREAMS === 'true') {
+          console.log("ElectricStreamSubscription chunk.value", chunk.value);
+        }
         controller.enqueue(chunk.value);
       },
     })
   )
   .pipeThrough(new LineTransformStream(this.url))
   .pipeThrough(
     new TransformStream({
       transform(chunk, controller) {
         for (const line of chunk) {
-          console.log("ElectricStreamSubscription line", line);
+          if (process.env.DEBUG_STREAMS === 'true') {
+            console.log("ElectricStreamSubscription line", line);
+          }
           controller.enqueue(safeParseJSON(line));
         }
       },
     })
   );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 30ea5eb and 5a86fe8.

📒 Files selected for processing (4)
  • .changeset/rude-walls-help.md (1 hunks)
  • apps/webapp/app/services/realtime/databaseRealtimeStreams.server.ts (2 hunks)
  • packages/core/src/v3/apiClient/runStream.ts (2 hunks)
  • packages/core/src/v3/apiClient/stream.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .changeset/rude-walls-help.md
🔇 Additional comments (2)
apps/webapp/app/services/realtime/databaseRealtimeStreams.server.ts (1)

47-49: LGTM: Stream termination condition is now more explicit

The simplified condition if (done || !value) properly handles both stream completion and empty chunks.

packages/core/src/v3/apiClient/stream.ts (1)

238-244: LGTM: Proper handling of incomplete data

The flush implementation correctly handles any remaining data in the buffer when the stream ends.

for (const line of chunk) {
console.log("ElectricStreamSubscription line", line);

controller.enqueue(safeParseJSON(line));
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider handling JSON parse errors explicitly

The safeParseJSON function silently returns the raw string on parse errors. Consider adding explicit error handling for malformed JSON.

-controller.enqueue(safeParseJSON(line));
+try {
+  const parsed = safeParseJSON(line);
+  if (typeof parsed === 'string' && line !== parsed) {
+    console.warn(`Failed to parse JSON for line: ${line}`);
+  }
+  controller.enqueue(parsed);
+} catch (error) {
+  console.error(`Error processing line: ${line}`, error);
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
controller.enqueue(safeParseJSON(line));
try {
const parsed = safeParseJSON(line);
if (typeof parsed === 'string' && line !== parsed) {
console.warn(`Failed to parse JSON for line: ${line}`);
}
controller.enqueue(parsed);
} catch (error) {
console.error(`Error processing line: ${line}`, error);
}

Copy link

pkg-pr-new bot commented Dec 11, 2024

@trigger.dev/build

npm i https://pkg.pr.new/triggerdotdev/trigger.dev/@trigger.dev/build@1549

@trigger.dev/core

npm i https://pkg.pr.new/triggerdotdev/trigger.dev/@trigger.dev/core@1549

trigger.dev

npm i https://pkg.pr.new/triggerdotdev/trigger.dev@1549

@trigger.dev/react-hooks

npm i https://pkg.pr.new/triggerdotdev/trigger.dev/@trigger.dev/react-hooks@1549

@trigger.dev/rsc

npm i https://pkg.pr.new/triggerdotdev/trigger.dev/@trigger.dev/rsc@1549

@trigger.dev/sdk

npm i https://pkg.pr.new/triggerdotdev/trigger.dev/@trigger.dev/sdk@1549

commit: d4f533f

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (7)
apps/webapp/server.ts (1)

84-95: Consider adding request logging for blocked endpoints.

The middleware implementation correctly restricts access to realtime endpoints while maintaining the healthcheck endpoint. However, adding logging for blocked requests would help with monitoring and debugging.

Consider adding logging like this:

 if (!req.url.startsWith("/realtime") && req.url !== "/healthcheck") {
+  console.log(`Blocked request to: ${req.method} ${req.url}`);
   res.status(404).send("Not Found");
   return;
 }
apps/webapp/test/authorizationRateLimitMiddleware.test.ts (2)

Line range hint 3-5: Consider documenting test timeout rationale

The 30-second timeout is set globally for all tests. Consider:

  1. Adding a comment explaining why 30 seconds was chosen
  2. Using per-test timeouts for long-running tests
  3. Documenting which tests might approach this timeout

Example documentation:

+ // Set a longer timeout for rate limiting tests as they include
+ // multiple sleep operations to test time-based behaviors
vi.setConfig({ testTimeout: 30_000 }); // 30 seconds timeout

Line range hint 171-315: Consider adding tests for concurrent scenarios

While the test suite is comprehensive, consider adding these scenarios:

  1. Concurrent requests hitting the rate limiter simultaneously
  2. Redis connection errors/timeouts
  3. Race conditions in token bucket refill

Example test structure:

redisTest("should handle concurrent requests correctly", async ({ redis }) => {
  // Setup rate limiter with small window
  // Fire multiple requests simultaneously using Promise.all
  // Verify correct number of requests succeeded/failed
});

redisTest("should handle Redis errors gracefully", async ({ redis }) => {
  // Setup rate limiter
  // Simulate Redis connection issues
  // Verify fallback behavior
});
packages/core/src/v3/runMetadata/metadataStream.ts (2)

68-69: Handle errors in async iterator returned by [Symbol.asyncIterator].

Errors occurring in the consumerStream may not be propagated to the consumer of the iterator.

Ensure that errors from the stream are properly caught and forwarded.


72-81: Manage cancellation in streamToAsyncIterator.

When the consumer stops iteration early, the underlying stream reader should be canceled to prevent resource leaks.

Modify the function to handle cancellation:

 async function* streamToAsyncIterator<T>(stream: ReadableStream<T>): AsyncIterableIterator<T> {
   const reader = stream.getReader();
   try {
     while (true) {
       const { done, value } = await reader.read();
       if (done) return;
       yield value;
     }
   } catch (error) {
     // Optionally handle errors from reader
     throw error;
   } finally {
+    await reader.cancel();
     reader.releaseLock();
   }
 }
packages/core/src/v3/runMetadata/manager.ts (2)

Line range hint 232-243: Inconsistent parameter naming in stream method.

The parameter value was renamed to source in the method signature but value is still used within the method. This inconsistency can lead to errors.

Apply this diff to ensure consistent naming:

 public async stream<T>(
     key: string,
-    value: AsyncIterable<T> | ReadableStream<T>,
+    source: AsyncIterable<T> | ReadableStream<T>,
     signal?: AbortSignal
 ): Promise<AsyncIterable<T>> {
-    const $value = value as AsyncIterable<T>;
+    const $source = source as AsyncIterable<T>;

     if (!this.runId) {
-        return $value;
+        return $source;
     }

     // ... existing code ...

         const streamInstance = new MetadataStream({
             key,
             runId: this.runId,
-            source: $value,
+            source: $source,
             baseUrl: this.streamsBaseUrl,
             headers: this.apiClient.getHeaders(),
             signal,
             version: this.streamsVersion,
         });

236-236: Redundant setting of $$streamsBaseUrl in metadata.

Setting $$streamsBaseUrl every time a new stream is created might be unnecessary if it doesn't change between streams.

Consider setting $$streamsBaseUrl once during initialization rather than on every stream creation.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 6a3a013 and d4f533f.

📒 Files selected for processing (7)
  • apps/webapp/server.ts (1 hunks)
  • apps/webapp/test/authorizationRateLimitMiddleware.test.ts (1 hunks)
  • packages/core/src/v3/runMetadata/manager.ts (1 hunks)
  • packages/core/src/v3/runMetadata/metadataStream.ts (2 hunks)
  • references/nextjs-realtime/package.json (1 hunks)
  • references/nextjs-realtime/src/components/TriggerButtonWithStreaming.tsx (0 hunks)
  • references/nextjs-realtime/src/trigger/ai.ts (0 hunks)
💤 Files with no reviewable changes (2)
  • references/nextjs-realtime/src/trigger/ai.ts
  • references/nextjs-realtime/src/components/TriggerButtonWithStreaming.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
packages/core/src/v3/runMetadata/metadataStream.ts

[error] 15-15: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

🔇 Additional comments (2)
references/nextjs-realtime/package.json (1)

10-11: LGTM! Deployment script added correctly.

The new deploy script is properly added and aligns with the existing trigger.dev CLI usage pattern.

Let's verify the trigger.dev CLI installation and version compatibility:

✅ Verification successful

Trigger.dev CLI and SDK versions are properly aligned

The verification confirms that both the CLI (trigger.dev) and SDK (@trigger.dev/sdk) packages are using the same workspace version ^3, ensuring compatibility for the deployment script to work correctly.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify trigger.dev CLI installation and version compatibility
# Expected: The CLI package should be listed in devDependencies with a matching version

# Check if trigger.dev CLI package exists in package.json
jq '.devDependencies["trigger.dev"]' references/nextjs-realtime/package.json

# Check for any potential version mismatches between SDK and CLI
jq '[.dependencies["@trigger.dev/sdk"], .devDependencies["trigger.dev"]]' references/nextjs-realtime/package.json

Length of output: 248

apps/webapp/test/authorizationRateLimitMiddleware.test.ts (1)

18-18: Consider alternatives to skipping tests in CI

Skipping these tests in GitHub Actions could lead to reduced test coverage in CI. Consider these alternatives:

  1. Set up Redis in GitHub Actions using service containers
  2. Mock Redis for CI environment
  3. Create a separate test suite for Redis-dependent tests

Let's check if other test files have similar patterns:

✅ Verification successful

Tests are intentionally skipped in CI due to Redis dependency

The skip pattern is consistent across Redis-dependent test files. The test file shows comprehensive Redis integration tests that:

  1. Use @internal/testcontainers for Redis setup
  2. Test various rate limiting scenarios including token bucket, fixed window, and sliding window
  3. Verify edge cases and caching behavior

Since these tests require a Redis instance and are properly tested in a containerized environment locally, skipping them in CI is a valid approach. No changes needed.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other test files that might be skipped in CI
rg -l "skipIf.*GITHUB_ACTIONS" "test/"

# Check if Redis is used in other test files
rg -l "redisTest" "test/"

Length of output: 154


Script:

#!/bin/bash
# Search for test files with skipIf and GITHUB_ACTIONS from repository root
fd -e test.ts -e test.js | xargs rg -l "skipIf.*GITHUB_ACTIONS"

# Check Redis usage in test files
fd -e test.ts -e test.js | xargs rg -l "redis"

# Look at the actual test file to understand Redis setup
fd "authorizationRateLimitMiddleware.test.ts" -x cat

Length of output: 13980

Comment on lines +84 to +95
if (process.env.ALLOW_ONLY_REALTIME_API === "true") {
// Block all requests that do not start with /realtime
app.use((req, res, next) => {
// Make sure /healthcheck is still accessible
if (!req.url.startsWith("/realtime") && req.url !== "/healthcheck") {
res.status(404).send("Not Found");
return;
}

next();
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

🛠️ Refactor suggestion

The review comment is valid. Static assets need to be allowed.

The verification confirms that the application serves static assets from two middleware configurations:

  • /build path serving from public/build with long-term caching
  • Root path / serving from public directory with 1-hour caching

The current REALTIME_API restriction would indeed block access to these essential static assets. The suggested improvement in the review to allow /build and /assets paths is correct, though it should also ensure the root public directory assets remain accessible.

Recommended paths to allow:

  • /realtime/*
  • /healthcheck
  • /build/*
  • /assets/*
  • Static files from public/*
🔗 Analysis chain

Verify impact on static assets and consider improvements.

The current implementation might block access to necessary static assets under /build and public directories. Additionally, the error response could be more informative for API consumers.

Let's verify the static asset paths that might be affected:

Consider these improvements:

 if (process.env.ALLOW_ONLY_REALTIME_API === "true") {
   app.use((req, res, next) => {
     // Make sure /healthcheck is still accessible
-    if (!req.url.startsWith("/realtime") && req.url !== "/healthcheck") {
+    if (!req.url.startsWith("/realtime") && 
+        !req.url.startsWith("/build") && 
+        !req.url.startsWith("/assets") && 
+        req.url !== "/healthcheck") {
       res.status(404).send("Not Found");
       return;
     }
     next();
   });
 }
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Find all static asset paths that might be affected
fd -t f . public/build -x echo "Static asset path:" {}

Length of output: 158


Script:

#!/bin/bash
# Let's first check if there are any static asset directories
fd -t d "^(public|build|assets|dist)$"

# Check for any static file serving middleware or route configurations
rg -A 5 "express\.static|app\.use.*static|serveStatic" apps/webapp/

Length of output: 769

Comment on lines +39 to +46
private initializeServerStream(): Promise<Response> {
const serverStream = this.serverStream.pipeThrough(
new TransformStream<T, string>({
async transform(chunk, controller) {
controller.enqueue(JSON.stringify(chunk) + "\n");
},
})
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ensure proper error handling in initializeServerStream.

The fetch request may fail or throw an exception, which isn't currently handled. This could lead to unhandled promise rejections.

Consider wrapping the fetch call in a try-catch block or handling errors appropriately.

+try {
   return fetch(
     `${this.options.baseUrl}/realtime/${this.options.version ?? "v1"}/streams/${
       this.options.runId
     }/${this.options.key}`,
     {
       method: "POST",
       headers: this.options.headers ?? {},
       body: serverStream,
       // @ts-expect-error
       duplex: "half",
       signal: this.controller.signal,
     }
   );
+} catch (error) {
+  // Handle error, possibly by rejecting the promise or logging
+  console.error('Failed to initialize server stream:', error);
+  throw error;
+}

Committable suggestion skipped: line range outside the PR's diff.

private serverIterator: AsyncIterator<T>;
private consumerIterator: AsyncIterator<T>;
private serverStream: ReadableStream<T>;
private consumerStream: ReadableStream<T>;
private streamPromise: Promise<void | Response>;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid using 'void' in union types for clarity.

Using void in a union type can be confusing. It is better to use undefined instead to represent the absence of a value.

Apply this diff to improve type clarity:

-private streamPromise: Promise<void | Response>;
+private streamPromise: Promise<Response | undefined>;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private streamPromise: Promise<void | Response>;
private streamPromise: Promise<Response | undefined>;
🧰 Tools
🪛 Biome (1.9.4)

[error] 15-15: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

Comment on lines +25 to +36
private createTeeStreams() {
const readableSource = new ReadableStream<T>({
start: async (controller) => {
for await (const value of this.options.source) {
controller.enqueue(value);
}
return queue.shift()!;

controller.close();
},
});

return {
serverIterator: teeIterator(this.serverQueue),
consumerIterator: teeIterator(this.consumerQueue),
};
return readableSource.tee();
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Handle errors during stream creation in createTeeStreams.

If an error occurs while iterating over this.options.source, it may not be properly propagated, leading to unhandled exceptions. Consider adding error handling to manage exceptions during iteration.

You can modify the start method to catch and forward errors:

 const readableSource = new ReadableStream<T>({
   start: async (controller) => {
+    try {
       for await (const value of this.options.source) {
         controller.enqueue(value);
       }
-      
       controller.close();
+    } catch (error) {
+      controller.error(error);
+    } finally {
+      controller.close();
+    }
   },
 });

Committable suggestion skipped: line range outside the PR's diff.

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.

1 participant