Skip to content

feat(promise-helpers): add finally callback to handleMaybePromise #2152

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

Merged

Conversation

EmrysMyrddin
Copy link
Collaborator

@EmrysMyrddin EmrysMyrddin commented Mar 7, 2025

This PR aims to ease the use of maybe async flows with handling of finally.

It contains 4 things:

  • handleMaybePromise can now take a finallyFactory that is guaranteed to always run, whatever happens in success or failure factories.
  • fakePromise previously could return a PromiseLike instead of a Promise (because of a weird unsound TS inference). It have been fix to know ponyfill Promise interface by only using then function of the provided PromiseLike object.
  • fakeRejectPromise no longer forward promises. Because it doesn't have much sense, this function should be used to fake a rejected promise, so the parameter should always be an error that will be encapsulated into a fake rejected promise
  • fakeRejectPromise and fakePromise are now safe against exceptions. This makes handleMaybePromise a bit obsolete, but it's way more easier to use classic promise chaining API than the callback style of handleMaybePromise
  • Added unfakePromise which turns a (possibly fake) Promise into a MaybePromise by extracting value or throwing error if the promise is fake.

Copy link
Contributor

coderabbitai bot commented Mar 7, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Enhanced asynchronous utilities with support for adding a finalization step after operations.
    • Introduced a utility to verify true asynchronous operations and improved error handling.
    • Added parameterized test support, making it easier to define tests with multiple cases.
  • Tests

    • Expanded the test suite to cover synchronous and asynchronous scenarios, ensuring robust operation and proper callback execution.

Walkthrough

The pull request enhances the handleMaybePromise function by introducing a new constant FAKE_PROMISE_SYMBOL_NAME and a function isActualPromise to validate true promises. The handleMaybePromise function has been modified to include an optional finallyFactory parameter for executing finalization logic after promise handling. The internal structure has been reworked to streamline promise creation and error management. Additionally, a comprehensive suite of Jest tests has been added to validate the function's behavior across various scenarios, ensuring robust handling of both promises and synchronous functions.

Changes

File(s) Change Summary
packages/promise-helpers/src/index.ts Added FAKE_PROMISE_SYMBOL_NAME constant; introduced isActualPromise for promise validation; updated handleMaybePromise overloads to include an optional finallyFactory; restructured promise handling and error management using fakePromise and fakeRejectPromise; added promiseLikeFinally and unfakePromise.
packages/promise-helpers/tests/handleMaybePromise.spec.ts Introduced a comprehensive Jest test suite to validate the behavior of handleMaybePromise, covering success, error handling, and synchronous function cases.
.changeset/late-flies-bet.md Documented the enhancement allowing a finally callback in handleMaybePromise, categorized as a minor update to the package.
deno-jest.ts Added it.each method for parameterized tests; modified export structure to enhance modularity.

Possibly related PRs

  • Introduce Promise Helpers #2102: The changes in the main PR, particularly the introduction of the handleMaybePromise function and modifications to promise handling, are directly related to the changes in the retrieved PR, which also focuses on enhancing promise functionality through the introduction of a helper package. Both PRs involve similar concepts of managing promises and promise-like objects.

Suggested reviewers

  • dotansimha
  • enisdenjo
  • ardatan

Poem

I’m hopping along in the code field so bright,
With a new finally step that makes promises right.
I scamper through functions with a joyful bound,
From success to error, a neat trail is found.
My tests confirm each leap and bound,
In this rabbit’s world, code magic is sound! 🐰
Keep hopping and coding, with delight all around!

Tip

⚡🧪 Multi-step agentic review comment chat (experimental)
  • We're introducing multi-step agentic chat in review comments. This experimental feature enhances review discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments.
    - To enable this feature, set early_access to true under in the settings.

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4034df8 and b685704.

📒 Files selected for processing (1)
  • deno-jest.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • deno-jest.ts
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: unit / deno
  • GitHub Check: unit / node 23
  • GitHub Check: unit / node 22
  • GitHub Check: unit / node 20
  • GitHub Check: unit / node 18
  • GitHub Check: prettier
  • GitHub Check: e2e / azure-function

🪧 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.
  • @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.

@EmrysMyrddin EmrysMyrddin marked this pull request as draft March 7, 2025 19:23
Copy link
Contributor

github-actions bot commented Mar 7, 2025

@benchmarks/node-fetch results (noConsumeBody)

   ✓ active_handles.................: avg=140.23876 min=24      med=141     max=181     p(90)=160     p(95)=165    
     data_received..................: 21 MB  685 kB/s
     data_sent......................: 13 MB  444 kB/s
     http_req_blocked...............: avg=3.78µs    min=611ns   med=1.42µs  max=5.02ms  p(90)=2.1µs   p(95)=2.45µs 
     http_req_connecting............: avg=1.95µs    min=0s      med=0s      max=4.96ms  p(90)=0s      p(95)=0s     
     http_req_duration..............: avg=22.25ms   min=5.26ms  med=21.76ms max=1s      p(90)=28.11ms p(95)=29.66ms
       { expected_response:true }...: avg=22.25ms   min=5.26ms  med=21.76ms max=1s      p(90)=28.11ms p(95)=29.66ms
     http_req_failed................: 0.00%  ✓ 0           ✗ 134329
     http_req_receiving.............: avg=36.51µs   min=9.14µs  med=24.95µs max=30.03ms p(90)=40µs    p(95)=46.75µs
     http_req_sending...............: avg=11.29µs   min=3.26µs  med=6.88µs  max=9.84ms  p(90)=10.54µs p(95)=14.68µs
     http_req_tls_handshaking.......: avg=0s        min=0s      med=0s      max=0s      p(90)=0s      p(95)=0s     
     http_req_waiting...............: avg=22.21ms   min=5.1ms   med=21.72ms max=1s      p(90)=28.07ms p(95)=29.6ms 
     http_reqs......................: 134329 4477.198739/s
     iteration_duration.............: avg=44.64ms   min=17.56ms med=43.26ms max=1.02s   p(90)=48.05ms p(95)=54.05ms
     iterations.....................: 67151  2238.149413/s
     vus............................: 100    min=100       max=100 
     vus_max........................: 100    min=100       max=100 

Copy link
Contributor

github-actions bot commented Mar 7, 2025

@benchmarks/node-fetch results (consumeBody)

   ✓ active_handles.................: avg=139.842211 min=49      med=139     max=192      p(90)=163     p(95)=167    
     data_received..................: 21 MB  682 kB/s
     data_sent......................: 13 MB  437 kB/s
     http_req_blocked...............: avg=3.22µs     min=631ns   med=1.22µs  max=5.24ms   p(90)=2.03µs  p(95)=2.27µs 
     http_req_connecting............: avg=1.46µs     min=0s      med=0s      max=4.87ms   p(90)=0s      p(95)=0s     
     http_req_duration..............: avg=22.35ms    min=5.43ms  med=21.8ms  max=895.66ms p(90)=28.31ms p(95)=29.82ms
       { expected_response:true }...: avg=22.35ms    min=5.43ms  med=21.8ms  max=895.66ms p(90)=28.31ms p(95)=29.82ms
     http_req_failed................: 0.00%  ✓ 0           ✗ 133759
     http_req_receiving.............: avg=32.96µs    min=9.03µs  med=23.11µs max=18.76ms  p(90)=37.16µs p(95)=43.62µs
     http_req_sending...............: avg=11.21µs    min=3.17µs  med=5.79µs  max=11.95ms  p(90)=9.53µs  p(95)=13.26µs
     http_req_tls_handshaking.......: avg=0s         min=0s      med=0s      max=0s       p(90)=0s      p(95)=0s     
     http_req_waiting...............: avg=22.31ms    min=5.39ms  med=21.77ms max=895.58ms p(90)=28.27ms p(95)=29.77ms
     http_reqs......................: 133759 4458.096664/s
     iteration_duration.............: avg=44.82ms    min=12.54ms med=43.34ms max=915.23ms p(90)=48.11ms p(95)=54.48ms
     iterations.....................: 66855  2228.231764/s
     vus............................: 100    min=100       max=100 
     vus_max........................: 100    min=100       max=100 

Copy link
Contributor

github-actions bot commented Mar 7, 2025

@benchmarks/server results (undici)

     ✓ no-errors
     ✓ expected-result

   ✓ checks.........................: 100.00% ✓ 215652      ✗ 0     
     data_received..................: 22 MB   722 kB/s
     data_sent......................: 8.6 MB  288 kB/s
     http_req_blocked...............: avg=1.42µs   min=902ns    med=1.2µs    max=1.53ms   p(90)=1.89µs   p(95)=2.06µs  
     http_req_connecting............: avg=1ns      min=0s       med=0s       max=113.52µs p(90)=0s       p(95)=0s      
     http_req_duration..............: avg=215.23µs min=159.98µs med=203.18µs max=60.91ms  p(90)=229.42µs p(95)=239.23µs
       { expected_response:true }...: avg=215.23µs min=159.98µs med=203.18µs max=60.91ms  p(90)=229.42µs p(95)=239.23µs
     http_req_failed................: 0.00%   ✓ 0           ✗ 107826
     http_req_receiving.............: avg=25.6µs   min=14.04µs  med=24.01µs  max=2.48ms   p(90)=31.06µs  p(95)=33.76µs 
     http_req_sending...............: avg=6.46µs   min=4.16µs   med=5.64µs   max=572.86µs p(90)=8.26µs   p(95)=8.99µs  
     http_req_tls_handshaking.......: avg=0s       min=0s       med=0s       max=0s       p(90)=0s       p(95)=0s      
     http_req_waiting...............: avg=183.16µs min=133.37µs med=171.2µs  max=60.83ms  p(90)=194.33µs p(95)=203.21µs
     http_reqs......................: 107826  3594.081761/s
     iteration_duration.............: avg=273.69µs min=203.45µs med=260.53µs max=61.07ms  p(90)=290.67µs p(95)=303.03µs
     iterations.....................: 107826  3594.081761/s
     vus............................: 1       min=1         max=1   
     vus_max........................: 1       min=1         max=1   

Copy link
Contributor

github-actions bot commented Mar 7, 2025

@benchmarks/server results (ponyfill)

     ✓ no-errors
     ✓ expected-result

   ✓ checks.........................: 100.00% ✓ 288132      ✗ 0     
     data_received..................: 28 MB   946 kB/s
     data_sent......................: 12 MB   384 kB/s
     http_req_blocked...............: avg=1.44µs   min=881ns    med=1.21µs   max=334.36µs p(90)=1.91µs   p(95)=2.1µs   
     http_req_connecting............: avg=0ns      min=0s       med=0s       max=118.29µs p(90)=0s       p(95)=0s      
     http_req_duration..............: avg=144.18µs min=96.96µs  med=138.78µs max=5.97ms   p(90)=162.37µs p(95)=170.69µs
       { expected_response:true }...: avg=144.18µs min=96.96µs  med=138.78µs max=5.97ms   p(90)=162.37µs p(95)=170.69µs
     http_req_failed................: 0.00%   ✓ 0           ✗ 144066
     http_req_receiving.............: avg=24.87µs  min=12.11µs  med=23.2µs   max=3.09ms   p(90)=30.94µs  p(95)=33.86µs 
     http_req_sending...............: avg=6.56µs   min=4.18µs   med=5.71µs   max=420.3µs  p(90)=8.36µs   p(95)=9.43µs  
     http_req_tls_handshaking.......: avg=0s       min=0s       med=0s       max=0s       p(90)=0s       p(95)=0s      
     http_req_waiting...............: avg=112.74µs min=71.77µs  med=107.16µs max=5.79ms   p(90)=127.17µs p(95)=134.01µs
     http_reqs......................: 144066  4802.041565/s
     iteration_duration.............: avg=203.53µs min=143.55µs med=197.35µs max=6.5ms    p(90)=224.55µs p(95)=235.84µs
     iterations.....................: 144066  4802.041565/s
     vus............................: 1       min=1         max=1   
     vus_max........................: 1       min=1         max=1   

Copy link
Contributor

github-actions bot commented Mar 7, 2025

@benchmarks/server results (native)

     ✓ no-errors
     ✓ expected-result

   ✓ checks.........................: 100.00% ✓ 229704      ✗ 0     
     data_received..................: 23 MB   770 kB/s
     data_sent......................: 9.2 MB  306 kB/s
     http_req_blocked...............: avg=1.43µs   min=911ns    med=1.21µs   max=180.63µs p(90)=1.92µs   p(95)=2.08µs  
     http_req_connecting............: avg=1ns      min=0s       med=0s       max=115.34µs p(90)=0s       p(95)=0s      
     http_req_duration..............: avg=198.53µs min=146.19µs med=188.69µs max=7.73ms   p(90)=214.56µs p(95)=224.24µs
       { expected_response:true }...: avg=198.53µs min=146.19µs med=188.69µs max=7.73ms   p(90)=214.56µs p(95)=224.24µs
     http_req_failed................: 0.00%   ✓ 0           ✗ 114852
     http_req_receiving.............: avg=25.58µs  min=13.59µs  med=23.89µs  max=3.61ms   p(90)=31.02µs  p(95)=33.79µs 
     http_req_sending...............: avg=6.39µs   min=4.06µs   med=5.5µs    max=2.94ms   p(90)=8.21µs   p(95)=8.92µs  
     http_req_tls_handshaking.......: avg=0s       min=0s       med=0s       max=0s       p(90)=0s       p(95)=0s      
     http_req_waiting...............: avg=166.55µs min=121.28µs med=156.73µs max=7.7ms    p(90)=179.53µs p(95)=188.27µs
     http_reqs......................: 114852  3828.252412/s
     iteration_duration.............: avg=256.73µs min=189.03µs med=245.99µs max=7.81ms   p(90)=275.33µs p(95)=287.86µs
     iterations.....................: 114852  3828.252412/s
     vus............................: 1       min=1         max=1   
     vus_max........................: 1       min=1         max=1   

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

🧹 Nitpick comments (3)
packages/promise-helpers/tests/handleMaybePromise.spec.ts (2)

26-33: Incorrect test description

The test description mentions "fake promise" but the test is actually validating error handling for a rejected promise.

-      it('should call finally and allow chaining with a fake promise', async () => {
+      it('should call finally and allow chaining with a rejected promise', async () => {

6-36: Additional test coverage needed

The tests only cover scenarios where all callbacks are provided. Consider adding tests for cases when the finallyFactory is not provided or when onError is not provided to ensure the error propagation works as expected.

Here's a suggestion for additional test cases:

it('should work correctly when finallyFactory is not provided', async () => {
  expect(
    await handleMaybePromise(() => Promise.resolve('test'), onSuccess, onError)
  ).toBe('test');
  expect(onSuccess).toHaveBeenCalledTimes(1);
  expect(onError).not.toHaveBeenCalled();
});

it('should throw when onError is not provided and promise rejects', async () => {
  await expect(
    handleMaybePromise(() => Promise.reject('error'), onSuccess)
  ).rejects.toBe('error');
  expect(onSuccess).not.toHaveBeenCalled();
});

it('should throw and still call finally when onError is not provided', async () => {
  await expect(
    handleMaybePromise(() => Promise.reject('error'), onSuccess, undefined, onFinally)
  ).rejects.toBe('error');
  expect(onSuccess).not.toHaveBeenCalled();
  expect(onFinally).toHaveBeenCalledTimes(1);
});
packages/promise-helpers/src/index.ts (1)

42-60: Consider simplifying the nested promise chains

The implementation works correctly but is complex with multiple nested calls to handleMaybePromise. This could potentially be simplified for better readability.

Consider whether Promise.prototype.finally could be directly used when available, with a fallback for environments where it's not supported or for non-promise returns. This might make the code more straightforward, especially since the finallyFactory is guaranteed to be called regardless of success or failure.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0d4a4e and 2a69c5b.

📒 Files selected for processing (2)
  • packages/promise-helpers/src/index.ts (2 hunks)
  • packages/promise-helpers/tests/handleMaybePromise.spec.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (12)
  • GitHub Check: unit / deno
  • GitHub Check: unit / node 23
  • GitHub Check: unit / node 22
  • GitHub Check: unit / node 20
  • GitHub Check: unit / node 18
  • GitHub Check: unit / bun
  • GitHub Check: server (undici)
  • GitHub Check: node-fetch (consumeBody)
  • GitHub Check: server (ponyfill)
  • GitHub Check: lint
  • GitHub Check: node-fetch (noConsumeBody)
  • GitHub Check: server (native)
🔇 Additional comments (5)
packages/promise-helpers/tests/handleMaybePromise.spec.ts (1)

17-24: LGTM! Test validates success path properly

This test correctly verifies that both the success callback and finally callback are called when a promise resolves successfully, and that the error callback is never invoked.

packages/promise-helpers/src/index.ts (4)

14-14: LGTM! Consistent type definitions for finallyFactory

The finallyFactory parameter is consistently defined across all three function overloads, providing type safety for this new feature.

Also applies to: 20-20, 26-26


42-63: The implementation correctly handles the new finallyFactory parameter

The conditional logic to incorporate the finallyFactory callback is cleanly implemented, ensuring that finallyFactory is called regardless of whether the promise resolves or rejects.


65-69: LGTM! Error handling improved with explicit throw

The error handling now properly throws the error when no outputErrorFactory is provided, which improves error propagation.


41-70:

✅ Verification successful

Verify backward compatibility

This change adds a new optional parameter which should maintain backward compatibility, but it's worth verifying that existing usage patterns still work as expected.


🏁 Script executed:

#!/bin/bash
# Search for existing usage of handleMaybePromise in the codebase
rg --type ts "handleMaybePromise\(" -A 3 -B 1

Length of output: 10192


Backward compatibility verified—no breakage observed.
After reviewing the test cases in packages/promise-helpers/tests/handleMaybePromise.spec.ts and various usage sites (e.g., in packages/server), it’s clear that the integration of the new optional parameter (finallyFactory) does not disrupt existing behavior. Both success and error handling paths remain consistent with previous patterns, confirming that existing usage patterns continue to work as expected.

@EmrysMyrddin EmrysMyrddin marked this pull request as ready for review March 8, 2025 08:29
Copy link
Contributor

github-actions bot commented Mar 8, 2025

🚀 Snapshot Release (alpha)

The latest changes of this PR are available as alpha on npm (based on the declared changesets):

Package Version Info
@whatwg-node/promise-helpers 1.3.0-alpha-20250317151531-b68570407835424594d03bc1c74dff044858b365 npm ↗︎ unpkg ↗︎

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: 0

🧹 Nitpick comments (1)
packages/promise-helpers/src/index.ts (1)

314-338: Well-implemented promiseLikeFinally helper function.

This helper function correctly handles both promises with native finally support and those without it. The implementation properly ensures that:

  1. For successful promises, the finally callback executes and the original result is preserved
  2. For failed promises, the finally callback executes and the original error is rethrown

Consider slightly simplifying the error handling path to reduce duplication:

-    err => {
-      const finallyRes = onFinally();
-      if (isPromise(finallyRes)) {
-        return finallyRes.then(() => {
-          throw err;
-        });
-      } else {
-        throw err;
-      }
-    },
+    err => {
+      const finallyRes = onFinally();
+      const throwError = () => { throw err; };
+      return isPromise(finallyRes) ? finallyRes.then(throwError) : throwError();
+    },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a69c5b and 49b6ba9.

📒 Files selected for processing (3)
  • .changeset/late-flies-bet.md (1 hunks)
  • packages/promise-helpers/src/index.ts (3 hunks)
  • packages/promise-helpers/tests/handleMaybePromise.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/promise-helpers/tests/handleMaybePromise.spec.ts
🧰 Additional context used
🪛 LanguageTool
.changeset/late-flies-bet.md

[grammar] ~5-~5: You used an adverb (‘finally’) instead of an adjective, or a noun (‘callback’) instead of another adjective.
Context: ...se-helpers': minor --- Allow to pass a finally callback to handleMaybePromise

(A_RB_NN)

🪛 Biome (1.9.4)
packages/promise-helpers/src/index.ts

[error] 170-170: Do not add then to an object.

(lint/suspicious/noThenProperty)

⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: unit / deno
  • GitHub Check: unit / node 23
  • GitHub Check: unit / bun
  • GitHub Check: unit / node 22
  • GitHub Check: unit / node 20
  • GitHub Check: unit / node 18
  • GitHub Check: node-fetch (noConsumeBody)
🔇 Additional comments (6)
packages/promise-helpers/src/index.ts (5)

10-15: Great addition of finallyFactory parameter to the function signature.

The introduction of an optional finallyFactory parameter enhances the API by allowing execution of cleanup logic regardless of promise outcome, similar to the standard Promise.finally() behavior.


28-40: Improved error handling implementation.

The refactored implementation effectively handles errors from both the input factory and promise chain. Using fakeRejectPromise(err) maintains consistent behavior by properly propagating errors through the promise chain.


42-44: Clean implementation of finally functionality.

Good approach using a dedicated helper function promiseLikeFinally to handle the finally logic. This keeps the main function clean while properly applying the finalization step.


57-57: Appropriate type signature enhancement.

Updating fakePromise to accept T | MaybePromiseLike<T> makes the function more flexible and consistent with its usage in the refactored handleMaybePromise.


170-174: Enhanced error handling in fakeRejectPromise.

The updated implementation properly handles rejection cases by checking if a rejection handler is provided before applying it.

🧰 Tools
🪛 Biome (1.9.4)

[error] 170-170: Do not add then to an object.

(lint/suspicious/noThenProperty)

.changeset/late-flies-bet.md (1)

1-5: Appropriate changeset description.

The changeset correctly documents the feature addition and marks it as a minor version bump, which is appropriate for new functionality that doesn't break backward compatibility.

🧰 Tools
🪛 LanguageTool

[grammar] ~5-~5: You used an adverb (‘finally’) instead of an adjective, or a noun (‘callback’) instead of another adjective.
Context: ...se-helpers': minor --- Allow to pass a finally callback to handleMaybePromise

(A_RB_NN)

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: 0

🧹 Nitpick comments (1)
packages/promise-helpers/src/index.ts (1)

10-13: Good addition of the isActualPromise utility function.

This new helper properly checks for all three Promise method properties, distinguishing between general Promise-like objects and fully-featured Promise objects.

However, the implementation could be improved by using optional chaining for better safety:

-  return maybePromise && maybePromise.then && maybePromise.catch && maybePromise.finally;
+  return maybePromise?.then && maybePromise?.catch && maybePromise?.finally;
🧰 Tools
🪛 Biome (1.9.4)

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c88e8c0 and 97047d4.

📒 Files selected for processing (1)
  • packages/promise-helpers/src/index.ts (3 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/promise-helpers/src/index.ts

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 71-71: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 182-182: Do not add then to an object.

(lint/suspicious/noThenProperty)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: unit / deno
  • GitHub Check: unit / node 23
  • GitHub Check: unit / bun
  • GitHub Check: unit / node 22
  • GitHub Check: unit / node 20
  • GitHub Check: unit / node 18
  • GitHub Check: server (ponyfill)
  • GitHub Check: node-fetch (consumeBody)
🔇 Additional comments (8)
packages/promise-helpers/src/index.ts (8)

19-19: LGTM: The new finallyFactory parameter is consistently added to all overloads.

This ensures type safety across all variants of handleMaybePromise.

Also applies to: 25-25, 31-31


33-38: Improved error handling with simplified initialization.

The implementation is cleaner now, using fakePromise and fakeRejectPromise directly for initialization.


40-45: Good refactoring of result handling flow.

The new approach with a single result$ variable improves code clarity. Using input$.then() with both success and error factories in a single call is more concise.


47-49: Good implementation of finalization logic.

The finalization is correctly applied conditionally and leverages the native Promise finally method.


51-59: Properly handles fake promises and rejections.

The logic for checking and unwrapping fake promises remains intact, ensuring consistent behavior with the previous implementation.


180-186: Updated signature and implementation of fakeRejectPromise.

The return type change from Promise<never> to Promise<T> is more flexible, and the implementation properly handles rejection cases.

Similar to the previous comment, there's a linting error for the then property. Consider addressing this consistently.

🧰 Tools
🪛 Biome (1.9.4)

[error] 182-182: Do not add then to an object.

(lint/suspicious/noThenProperty)


326-350: Well-implemented promiseLikeFinally utility function.

This helper function correctly implements the finalization logic for both Promise objects with native finally support and Promise-like objects without it.

The implementation handles both success and error paths properly, ensuring the finalization callback is always executed while preserving the original promise result or error.


62-76:

❓ Verification inconclusive

Improved fakePromise function with better type support.

The updated function handles both MaybePromise<T> and MaybePromiseLike<T> appropriately, using the new isActualPromise check.

The implementation of the then method in line 71 has a linting error. Consider restructuring to avoid the "no then property" warning.


🏁 Script executed:

#!/bin/bash
# Check if there's a way to silence this specific linting error in the codebase
grep -r "noThenProperty" --include="*.json" .

Length of output: 45


Attention: Verify Linting Warning on then Method Implementation

  • The revised fakePromise function now handles both MaybePromise<T> and MaybePromiseLike<T> correctly using the new isActualPromise check, which is a solid improvement.
  • However, the inline implementation of the then method on line 71 currently triggers a lint warning regarding the "no then property" rule.
  • Our initial search for any configuration to suppress this rule (e.g., in JSON-based lint settings) returned no results, indicating that the warning isn’t being silenced globally.
  • Action Required: Please consider refactoring the then method (line 71) to adhere to your linting guidelines—this might involve restructuring the promise-like object construction or delegating to a helper—to avoid this specific warning.
🧰 Tools
🪛 Biome (1.9.4)

[error] 71-71: Do not add then to an object.

(lint/suspicious/noThenProperty)

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: 0

🧹 Nitpick comments (1)
packages/promise-helpers/src/index.ts (1)

10-13: Consider using optional chaining for safer property access.

The isActualPromise function correctly checks for the presence of then, catch, and finally methods, but you should use optional chaining to prevent potential runtime errors if the passed value is null or undefined.

export function isActualPromise<T>(value: MaybePromiseLike<T>): value is Promise<T> {
  const maybePromise = value as any;
-  return maybePromise && maybePromise.then && maybePromise.catch && maybePromise.finally;
+  return !!maybePromise?.then && !!maybePromise?.catch && !!maybePromise?.finally;
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 97047d4 and 848de1d.

📒 Files selected for processing (1)
  • packages/promise-helpers/src/index.ts (3 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/promise-helpers/src/index.ts

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 64-64: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 72-72: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 82-82: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 183-183: Do not add then to an object.

(lint/suspicious/noThenProperty)

⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: unit / deno
  • GitHub Check: unit / node 23
  • GitHub Check: unit / node 22
  • GitHub Check: unit / node 20
  • GitHub Check: unit / node 18
🔇 Additional comments (6)
packages/promise-helpers/src/index.ts (6)

15-32: Function signature extensions look good.

The addition of the finallyFactory parameter to all overloads of handleMaybePromise is consistent and well-typed.


33-60: Improved error handling and promise flow.

The restructured implementation properly handles promise creation, error management, and the new finally functionality. The code now clearly separates input promise creation, output promise creation, and finally handling.


62-65: Type signature correctly updated.

The function signature for fakePromise now properly reflects that it works with both MaybePromise<T> and MaybePromiseLike<T> types, enhancing type inference.

🧰 Tools
🪛 Biome (1.9.4)

[error] 64-64: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


70-77: Implementation correctly handles PromiseLike objects.

The new implementation properly creates a Promise-like interface by utilizing only the then method of the provided PromiseLike object, addressing the TypeScript inference issue mentioned in the PR objectives.

Note: There's a static analysis warning about then property, but this is a false positive as we're implementing a Promise-compatible interface.

🧰 Tools
🪛 Biome (1.9.4)

[error] 72-72: Do not add then to an object.

(lint/suspicious/noThenProperty)


181-186: Type signature and implementation are now more flexible.

The updated fakeRejectPromise function correctly returns Promise<T> instead of Promise<never>, making it more versatile for error handling across different contexts.

🧰 Tools
🪛 Biome (1.9.4)

[error] 183-183: Do not add then to an object.

(lint/suspicious/noThenProperty)


327-351: Well-implemented Promise.finally polyfill.

The promiseLikeFinally function properly implements the Promise.finally behavior for objects that may not have native support. The implementation correctly handles both success and error cases, ensuring that the finally callback is always executed and the original result is properly propagated.

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: 0

🧹 Nitpick comments (2)
packages/promise-helpers/src/index.ts (2)

10-13: Consider using optional chaining for more robust code

The new isActualPromise function correctly checks for the presence of all methods needed for an actual Promise, but could benefit from optional chaining to be more robust against edge cases.

export function isActualPromise<T>(value: MaybePromiseLike<T>): value is Promise<T> {
  const maybePromise = value as any;
-  return maybePromise && maybePromise.then && maybePromise.catch && maybePromise.finally;
+  return maybePromise?.then && maybePromise?.catch && maybePromise?.finally;
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


64-64: Consider using 'undefined' instead of 'void' for parameter types

The static analyzer flagged the use of void as potentially confusing outside a return type or type parameter.

-export function fakePromise(value: void): Promise<void>;
+export function fakePromise(value: undefined): Promise<void>;
🧰 Tools
🪛 Biome (1.9.4)

[error] 64-64: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 848de1d and 808d86c.

📒 Files selected for processing (1)
  • packages/promise-helpers/src/index.ts (4 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/promise-helpers/src/index.ts

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 64-64: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 72-72: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 82-82: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 183-183: Do not add then to an object.

(lint/suspicious/noThenProperty)

⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: unit / deno
  • GitHub Check: unit / node 23
  • GitHub Check: unit / node 22
  • GitHub Check: unit / node 20
  • GitHub Check: unit / bun
  • GitHub Check: unit / node 18
  • GitHub Check: server (undici)
  • GitHub Check: server (ponyfill)
  • GitHub Check: node-fetch (consumeBody)
  • GitHub Check: server (native)
  • GitHub Check: node-fetch (noConsumeBody)
🔇 Additional comments (6)
packages/promise-helpers/src/index.ts (6)

15-32: LGTM! Consistent addition of finallyFactory parameter to function overloads

The addition of the optional finallyFactory parameter to all overloads of handleMaybePromise maintains consistent function signatures and improves the API by allowing finalization logic to run regardless of promise resolution status.


33-60: Improved error handling and promise flow with finallyFactory implementation

The restructured implementation now properly initializes and handles the input promise, applies error handling, and correctly implements the finalization step. The code structure is more robust and aligns well with Promise semantics.


62-77: Correct Promise behavior implementation with appropriate instanceof check

The updated fakePromise function now correctly checks for actual Promises using the new helper function and ensures proper propagation of Promise behavior.

🧰 Tools
🪛 Biome (1.9.4)

[error] 64-64: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 72-72: Do not add then to an object.

(lint/suspicious/noThenProperty)


82-104: Enhanced error handling in promise then and finally methods

The error handling for the then and finally methods has been improved with try-catch blocks to properly propagate errors through the promise chain.

🧰 Tools
🪛 Biome (1.9.4)

[error] 82-82: Do not add then to an object.

(lint/suspicious/noThenProperty)


181-216: Type signature improvement for fakeRejectPromise

The updated generic type signature Promise<T> (instead of Promise<never>) provides more flexibility in how this function can be used while maintaining type safety.

🧰 Tools
🪛 Biome (1.9.4)

[error] 183-183: Do not add then to an object.

(lint/suspicious/noThenProperty)


339-363: Well-implemented Promise.finally polyfill

The new promiseLikeFinally function correctly implements the finally method for PromiseLike objects that don't have a native implementation. It properly handles both success and error paths, and preserves the semantics of finally by returning the original value or rethrowing the original error after the callback executes.

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: 0

🧹 Nitpick comments (3)
packages/promise-helpers/src/index.ts (3)

10-13: Great addition of the isActualPromise function for more precise Promise detection.

However, consider enhancing robustness by checking for function types rather than just property existence:

export function isActualPromise<T>(value: MaybePromiseLike<T>): value is Promise<T> {
  const maybePromise = value as any;
-  return maybePromise && maybePromise.then && maybePromise.catch && maybePromise.finally;
+  return maybePromise && 
+         typeof maybePromise.then === 'function' && 
+         typeof maybePromise.catch === 'function' && 
+         typeof maybePromise.finally === 'function';
}

This ensures that the properties are actually callable methods.

🧰 Tools
🪛 Biome (1.9.4)

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


50-53: Consider replacing void with undefined in the parameter type.

The static analyzer correctly flags that void is confusing outside a return type or a type parameter. Consider using undefined instead:

export function fakePromise<T>(value: MaybePromise<T>): Promise<T>;
export function fakePromise<T>(value: MaybePromiseLike<T>): Promise<T>;
-export function fakePromise(value: void): Promise<void>;
+export function fakePromise(value: undefined): Promise<void>;
export function fakePromise<T>(value: MaybePromiseLike<T>): Promise<T> {
🧰 Tools
🪛 Biome (1.9.4)

[error] 52-52: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


327-351: Well-implemented promiseLikeFinally utility function.

This function nicely handles the case where a PromiseLike object might not have a finally method. However, the error handling could be more concise:

  return value.then(
    res => {
      const finallyRes = onFinally();
      return isPromise(finallyRes) ? finallyRes.then(() => res) : res;
    },
    err => {
      const finallyRes = onFinally();
-      if (isPromise(finallyRes)) {
-        return finallyRes.then(() => {
-          throw err;
-        });
-      } else {
-        throw err;
-      }
+      return isPromise(finallyRes) 
+        ? finallyRes.then(() => { throw err; }) 
+        : Promise.reject(err);
    },
  );

This refactoring makes the success and error branches more symmetrical and easier to understand.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 808d86c and f577f15.

📒 Files selected for processing (1)
  • packages/promise-helpers/src/index.ts (4 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/promise-helpers/src/index.ts

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 52-52: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 60-60: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 70-70: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 171-171: Do not add then to an object.

(lint/suspicious/noThenProperty)

⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: e2e / cloudflare-modules
  • GitHub Check: e2e / cloudflare-workers
  • GitHub Check: e2e / azure-function
  • GitHub Check: e2e / aws-lambda
  • GitHub Check: server (undici)
  • GitHub Check: alpha / snapshot
  • GitHub Check: server (ponyfill)
  • GitHub Check: node-fetch (consumeBody)
  • GitHub Check: server (native)
  • GitHub Check: node-fetch (noConsumeBody)
🔇 Additional comments (4)
packages/promise-helpers/src/index.ts (4)

15-48: The finallyFactory addition improves the API by supporting cleanup operations.

The restructured implementation is cleaner and more maintainable. The chain-based approach in line 33 improves readability, and the conditional handling of the finallyFactory is well-implemented.


54-65: Improved fakePromise handling of promise-like objects.

The implementation now correctly handles actual Promises, PromiseLike objects, and regular values. The delegation pattern for PromiseLike objects in lines 58-65 is well-implemented.

🧰 Tools
🪛 Biome (1.9.4)

[error] 60-60: Do not add then to an object.

(lint/suspicious/noThenProperty)


70-95: Improved error handling in fakePromise methods.

The addition of try-catch blocks in the then and finally methods is a significant improvement, making the fake promise robust against synchronous errors.

🧰 Tools
🪛 Biome (1.9.4)

[error] 70-70: Do not add then to an object.

(lint/suspicious/noThenProperty)


169-204: Generics improvement and better error handling in fakeRejectPromise.

Changing the return type from Promise<never> to Promise<T> makes the function more flexible. The improved error handling with try-catch blocks in the Promise methods is also a valuable enhancement.

🧰 Tools
🪛 Biome (1.9.4)

[error] 171-171: Do not add then to an object.

(lint/suspicious/noThenProperty)

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: 0

🧹 Nitpick comments (1)
packages/promise-helpers/src/index.ts (1)

10-13: Consider using optional chaining for safer property access

The implementation of isActualPromise should use optional chaining to avoid potential errors when accessing properties on null or undefined values.

export function isActualPromise<T>(value: MaybePromiseLike<T>): value is Promise<T> {
  const maybePromise = value as any;
-  return maybePromise && maybePromise.then && maybePromise.catch && maybePromise.finally;
+  return Boolean(maybePromise?.then && maybePromise?.catch && maybePromise?.finally);
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f577f15 and c964a81.

📒 Files selected for processing (1)
  • packages/promise-helpers/src/index.ts (4 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/promise-helpers/src/index.ts

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 44-44: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 52-52: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 62-62: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 163-163: Do not add then to an object.

(lint/suspicious/noThenProperty)

⏰ Context from checks skipped due to timeout of 90000ms (17)
  • GitHub Check: unit / node 23
  • GitHub Check: unit / node 22
  • GitHub Check: unit / node 20
  • GitHub Check: unit / deno
  • GitHub Check: unit / bun
  • GitHub Check: unit / node 18
  • GitHub Check: type check
  • GitHub Check: server (undici)
  • GitHub Check: alpha / snapshot
  • GitHub Check: prettier
  • GitHub Check: server (ponyfill)
  • GitHub Check: esm
  • GitHub Check: node-fetch (consumeBody)
  • GitHub Check: lint
  • GitHub Check: server (native)
  • GitHub Check: node-fetch (noConsumeBody)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (9)
packages/promise-helpers/src/index.ts (9)

15-40: Great implementation of the finally callback feature

The updated handleMaybePromise function now properly supports a finallyFactory parameter which executes after the promise is settled, regardless of success or failure. The implementation is clean and follows promise semantics correctly.


42-57: Enhanced type safety and promise handling

The updated fakePromise function signatures and implementation correctly return a Promise<T> instead of just a PromiseLike<T>. The use of isActualPromise helps detect and handle true promises properly.

🧰 Tools
🪛 Biome (1.9.4)

[error] 44-44: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 52-52: Do not add then to an object.

(lint/suspicious/noThenProperty)


65-68: Improved error handling with try-catch

The error handling in fakePromise.then is now safer with the use of try-catch blocks, which aligns with the PR objective to make the functions safe against exceptions.


77-84: Proper error handling in finally callback

The addition of try-catch in the finally method ensures that errors in the callback are properly converted to rejected promises, which improves the overall robustness of the library.


161-170: Enhanced type flexibility in fakeRejectPromise

The updated signature of fakeRejectPromise now returns Promise<T> instead of Promise<never>, allowing for more flexible error handling. The implementation now also properly handles errors that might occur during rejection handling.

🧰 Tools
🪛 Biome (1.9.4)

[error] 163-163: Do not add then to an object.

(lint/suspicious/noThenProperty)


175-179: Improved error propagation in catch method

The try-catch block in the catch method ensures proper error handling when callbacks throw exceptions, making the function safer to use.


185-189: Safe finally callback execution

The try-catch block in the finally method ensures proper error handling when callbacks throw exceptions, making the function more robust.


319-343: Well-implemented promiseLikeFinally utility

This new utility function correctly implements the finally behavior for PromiseLike objects that don't natively support it. It handles both success and error cases appropriately, ensuring that:

  1. The onFinally callback is always called
  2. The original resolved value is preserved
  3. Original errors are re-thrown after the callback executes

345-355: Clean implementation of unfakePromise

The unfakePromise function effectively converts fake promises back to their original values and handles error propagation correctly. This function simplifies the implementation of handleMaybePromise by abstracting the conversion logic.

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: 0

🧹 Nitpick comments (6)
packages/promise-helpers/src/index.ts (6)

11-14: Consider using optional chaining for safer property access

The isActualPromise function checks for the presence of Promise methods, but it could benefit from optional chaining to avoid potential null/undefined errors when accessing properties.

export function isActualPromise<T>(value: MaybePromiseLike<T>): value is Promise<T> {
-  const maybePromise = value as any;
-  return maybePromise && maybePromise.then && maybePromise.catch && maybePromise.finally;
+  const maybePromise = value as any;
+  return Boolean(maybePromise?.then && maybePromise?.catch && maybePromise?.finally);
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 13-13: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


43-58: Remove noThenProperty linting issues in fakePromise

The static analysis is flagging the use of then as a property name. While this is necessary for Promise implementation, consider adding a linting exception comment to clarify this is intentional.

export function fakePromise<T>(value: MaybePromiseLike<T>): Promise<T> {
  if (value && isActualPromise(value)) {
    return value;
  }

  if (isPromise(value)) {
    return {
+      // eslint-disable-next-line no-restricted-syntax
      then: (resolve, reject) => fakePromise(value.then(resolve, reject)),
      catch: reject => fakePromise(value.then(res => res, reject)),
      finally: cb => fakePromise(cb ? promiseLikeFinally(value, cb) : value),
      [Symbol.toStringTag]: 'Promise',
    };
  }
🧰 Tools
🪛 Biome (1.9.4)

[error] 45-45: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 53-53: Do not add then to an object.

(lint/suspicious/noThenProperty)


63-70: Simplify error handling in then method

The current implementation has a try/catch block that just rethrows the error, which could be simplified.

    then(resolve) {
      if (resolve) {
-        try {
-          return fakePromise(resolve(value));
-        } catch (err) {
-          throw err;
-          // return fakeRejectPromise(err);
-        }
+        return fakePromise(resolve(value));
      }
      return this;
    },
🧰 Tools
🪛 Biome (1.9.4)

[error] 68-68: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 63-63: Do not add then to an object.

(lint/suspicious/noThenProperty)


77-90: Simplify error handling in finally method

Similar to the then method, the error handling here can be simplified.

    finally(cb) {
      if (cb) {
-        try {
-          return fakePromise(cb()).then(
-            () => value,
-            () => value,
-          );
-        } catch (err) {
-          throw err;
-          // return fakeRejectPromise(err);
-        }
+        return fakePromise(cb()).then(
+          () => value,
+          () => value,
+        );
      }
      return this;
    },
🧰 Tools
🪛 Biome (1.9.4)

[error] 85-85: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


164-184: Simplify error handling in fakeRejectPromise

The current implementation has try/catch blocks that just rethrow the error in multiple places, which could be simplified.

export function fakeRejectPromise<T>(error: unknown): Promise<T> {
  return {
    then(_resolve, reject) {
      if (reject) {
-        try {
-          return fakePromise(reject(error));
-        } catch (err) {
-          throw err;
-          // return fakeRejectPromise(err);
-        }
+        return fakePromise(reject(error));
      }
      return this;
    },
    catch(reject: (error: unknown) => any) {
      if (reject) {
-        try {
-          return fakePromise(reject(error));
-        } catch (err) {
-          throw err;
-          // return fakeRejectPromise(err);
-        }
+        return fakePromise(reject(error));
      }
      return this;
    },
🧰 Tools
🪛 Biome (1.9.4)

[error] 171-171: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 182-182: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 166-166: Do not add then to an object.

(lint/suspicious/noThenProperty)


188-198: Simplify error handling in finally method of fakeRejectPromise

Similar to other methods, the error handling here can be simplified.

    finally(cb) {
      if (cb) {
-        try {
-          cb();
-        } catch (err) {
-          throw err;
-          // return fakeRejectPromise(err);
-        }
+        cb();
      }
      return this;
    },
🧰 Tools
🪛 Biome (1.9.4)

[error] 193-193: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c964a81 and 26c64dc.

📒 Files selected for processing (1)
  • packages/promise-helpers/src/index.ts (5 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/promise-helpers/src/index.ts

[error] 13-13: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 45-45: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 68-68: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 53-53: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 63-63: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 85-85: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 171-171: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 182-182: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 193-193: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 166-166: Do not add then to an object.

(lint/suspicious/noThenProperty)

⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: unit / node 23
  • GitHub Check: unit / node 22
  • GitHub Check: unit / deno
  • GitHub Check: unit / node 20
  • GitHub Check: unit / node 18
  • GitHub Check: server (undici)
  • GitHub Check: server (ponyfill)
  • GitHub Check: lint
  • GitHub Check: server (native)
🔇 Additional comments (3)
packages/promise-helpers/src/index.ts (3)

16-40: LGTM: Well-structured addition of finallyFactory parameter

The addition of the finallyFactory parameter to handleMaybePromise is well-implemented. The code correctly handles the finally callback regardless of whether the promise resolves or rejects.


325-349: LGTM: Well-implemented promiseLikeFinally function

The promiseLikeFinally function correctly implements the finally functionality for Promise-like objects, ensuring the finalization logic runs regardless of the promise outcome.


351-361: LGTM: Clean implementation of unfakePromise

The unfakePromise function elegantly unwraps fake promises to their original values, making the code in handleMaybePromise cleaner and more maintainable.

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: 0

🧹 Nitpick comments (4)
packages/promise-helpers/src/index.ts (4)

11-14: Use optional chaining for more robust property checks

The isActualPromise function checks multiple properties of the potential promise object. Using optional chaining would make this code more robust against edge cases.

export function isActualPromise<T>(value: MaybePromiseLike<T>): value is Promise<T> {
  const maybePromise = value as any;
-  return maybePromise && maybePromise.then && maybePromise.catch && maybePromise.finally;
+  return !!maybePromise?.then && !!maybePromise?.catch && !!maybePromise?.finally;
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 13-13: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


56-71: Consider simplifying the fakePromise overloads

The first two overloads could potentially be combined since they both return Promise<T>.

-export function fakePromise<T>(value: MaybePromise<T>): Promise<T>;
-export function fakePromise<T>(value: MaybePromiseLike<T>): Promise<T>;
+export function fakePromise<T>(value: MaybePromiseLike<T>): Promise<T>;
export function fakePromise(value: void): Promise<void>;
🧰 Tools
🪛 Biome (1.9.4)

[error] 58-58: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 66-66: Do not add then to an object.

(lint/suspicious/noThenProperty)


76-106: Remove unnecessary catch clauses that only rethrow errors

There are several instances where a catch clause simply rethrows the error without additional handling, making the try/catch redundant. The commented-out code suggests the previous behavior involved returning a fake rejected promise.

then(resolve) {
  if (resolve) {
-    try {
      return fakePromise(resolve(value));
-    } catch (err) {
-      throw err;
-      // return fakeRejectPromise(err);
-    }
  }
  return this;
},

Apply the same pattern to the other similar instances in the file (lines 90-101, 179-187, 190-198, 201-209).

🧰 Tools
🪛 Biome (1.9.4)

[error] 81-81: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 98-98: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 76-76: Do not add then to an object.

(lint/suspicious/noThenProperty)


234-236: Update mapMaybePromise to use the finallyFactory parameter

mapMaybePromise is a wrapper around handleMaybePromise, but it doesn't forward the finallyFactory parameter to it. Consider updating its signature to maintain API compatibility.

export function mapMaybePromise<TInput, TOutput>(
  input: MaybePromiseLike<TInput>,
  onSuccess: (value: TInput) => MaybePromiseLike<TOutput>,
  onError?: (err: any) => MaybePromiseLike<TOutput>,
+  finallyFactory?: () => MaybePromiseLike<void>,
): MaybePromiseLike<TOutput> {
-  return handleMaybePromise(() => input, onSuccess, onError);
+  return handleMaybePromise(() => input, onSuccess, onError, finallyFactory);
}

This change should also be applied to the other overloads of mapMaybePromise.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26c64dc and 647d37e.

📒 Files selected for processing (1)
  • packages/promise-helpers/src/index.ts (5 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/promise-helpers/src/index.ts

[error] 13-13: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 58-58: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 81-81: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 66-66: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 76-76: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 98-98: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 184-184: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 195-195: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 206-206: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 179-179: Do not add then to an object.

(lint/suspicious/noThenProperty)

⏰ Context from checks skipped due to timeout of 90000ms (16)
  • GitHub Check: unit / deno
  • GitHub Check: unit / node 23
  • GitHub Check: esm
  • GitHub Check: unit / node 22
  • GitHub Check: unit / bun
  • GitHub Check: unit / node 20
  • GitHub Check: unit / node 18
  • GitHub Check: type check
  • GitHub Check: lint
  • GitHub Check: server (undici)
  • GitHub Check: alpha / snapshot
  • GitHub Check: server (ponyfill)
  • GitHub Check: node-fetch (consumeBody)
  • GitHub Check: server (native)
  • GitHub Check: node-fetch (noConsumeBody)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
packages/promise-helpers/src/index.ts (5)

16-33: LGTM! Good addition of the finallyFactory parameter

The addition of the finallyFactory parameter to all overloads of handleMaybePromise is well-structured. This allows for executing finalization logic regardless of promise resolution status.


34-54: Clean restructuring of the handleMaybePromise function implementation

The restructured implementation makes the flow more explicit, with clear separation between input promise creation, error handling, and result handling. The addition of the finally functionality in lines 49-51 implements the core feature of this PR correctly.


177-215: Improved error handling in fakeRejectPromise

The updated implementation of fakeRejectPromise correctly ensures that all promise methods are properly defined and handles errors consistently. The signature change from Promise<never> to Promise<T> makes the function more flexible for different use cases.

🧰 Tools
🪛 Biome (1.9.4)

[error] 184-184: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 195-195: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 206-206: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 179-179: Do not add then to an object.

(lint/suspicious/noThenProperty)


338-362: Well-implemented promiseLikeFinally utility function

The new promiseLikeFinally function is a good addition that provides a consistent way to add finalization logic to promises, with fallback behavior for promise-like objects that don't have a native finally method.


364-374: Clean implementation of unfakePromise

The unfakePromise function correctly handles both successful and rejected fake promises, either by returning their value or throwing their error. This simplifies the external API by hiding the internal implementation details of fake promises.

@EmrysMyrddin EmrysMyrddin force-pushed the feature/promise-helpers/handle-maybe-promise-finnally branch from 647d37e to 06310a2 Compare March 9, 2025 21:29
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: 0

🧹 Nitpick comments (3)
packages/promise-helpers/src/index.ts (3)

10-13: Solid addition with a minor improvement opportunity.

The isActualPromise function effectively differentiates between promises and promise-likes by checking for all three standard Promise methods. This will be useful for more precise type checking in the codebase.

Consider using optional chaining for a more robust implementation:

-  return maybePromise && maybePromise.then && maybePromise.catch && maybePromise.finally;
+  return maybePromise?.then && maybePromise?.catch && maybePromise?.finally;
🧰 Tools
🪛 Biome (1.9.4)

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


42-45: Improved function signature with a minor type issue.

The signatures now properly handle both MaybePromise<T> and MaybePromiseLike<T>, making the function more flexible.

TypeScript prefers undefined over void for parameter types:

-export function fakePromise(value: void): Promise<void>;
+export function fakePromise(value: undefined): Promise<void>;
🧰 Tools
🪛 Biome (1.9.4)

[error] 44-44: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


46-58: Remove commented-out code.

The commented-out code blocks should be removed as they add noise without providing value. If this code represents alternative implementations that were considered, it would be better documented in PR comments or commit messages rather than in the source code.

-  // if (value && isActualPromise(value)) {
-  //   return value;
-  // }
-
-  // if (isPromise(value)) {
-  //   return {
-  //     then: (resolve, reject) => fakePromise(value.then(resolve, reject)),
-  //     catch: reject => fakePromise(value.then(res => res, reject)),
-  //     finally: cb => fakePromise(cb ? promiseLikeFinally(value, cb) : value),
-  //     [Symbol.toStringTag]: 'Promise',
-  //   };
-  // }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 647d37e and 06310a2.

📒 Files selected for processing (1)
  • packages/promise-helpers/src/index.ts (4 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/promise-helpers/src/index.ts

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 44-44: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 66-66: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 167-167: Do not add then to an object.

(lint/suspicious/noThenProperty)

⏰ Context from checks skipped due to timeout of 90000ms (17)
  • GitHub Check: unit / node 23
  • GitHub Check: unit / node 22
  • GitHub Check: unit / deno
  • GitHub Check: unit / node 20
  • GitHub Check: unit / bun
  • GitHub Check: esm
  • GitHub Check: unit / node 18
  • GitHub Check: lint
  • GitHub Check: prettier
  • GitHub Check: type check
  • GitHub Check: alpha / snapshot
  • GitHub Check: server (undici)
  • GitHub Check: server (ponyfill)
  • GitHub Check: node-fetch (consumeBody)
  • GitHub Check: server (native)
  • GitHub Check: node-fetch (noConsumeBody)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (8)
packages/promise-helpers/src/index.ts (8)

19-19: LGTM! The finallyFactory parameter is a good addition.

Adding the optional finallyFactory parameter to all overloads of handleMaybePromise properly implements the main objective of this PR. The typing is appropriate and maintains consistency across all signature variants.

Also applies to: 25-25, 31-31


33-39: Excellent refactoring of the implementation.

The implementation has been elegantly simplified by leveraging fakePromise for chaining, which handles both synchronous and asynchronous cases. The conditional application of finallyFactory is clean, and using unfakePromise at the end ensures that we maintain the expected return type.


59-61: Good simplification of the Promise handling logic.

Directly returning the Promise when the input is already a Promise-like object simplifies the code and improves performance by avoiding unnecessary wrapping.


68-72: Excellent error handling improvements.

Adding proper try-catch blocks to handle exceptions in callbacks is a significant improvement that makes the promise implementation more robust. Using fakeRejectPromise to propagate errors maintains the expected Promise behavior chain.

Also applies to: 81-88


165-165: Improved type signature for better generic support.

Changing the return type from Promise<never> to Promise<T> makes the function more versatile and enables better type inference when used in generic contexts. This aligns with the PR's goal of making promise utilities more flexible.


167-174: Enhanced error handling in reject promise implementation.

The improved implementation with try-catch blocks ensures that exceptions in callbacks are properly caught and propagated through the promise chain, making the fake promise implementation more robust and closer to native Promise behavior.

Also applies to: 179-183, 189-193

🧰 Tools
🪛 Biome (1.9.4)

[error] 167-167: Do not add then to an object.

(lint/suspicious/noThenProperty)


323-347: Well-implemented finally polyfill for Promise-like objects.

The promiseLikeFinally function is a solid implementation that correctly handles both success and error cases, ensuring that the finally callback is executed in both scenarios while preserving the original promise's resolution value or rejection reason.


349-359: Great utility for promise unwrapping.

The unfakePromise function elegantly handles both fake promises and fake rejected promises, extracting their values or propagating errors appropriately. This supports the refactored implementation of handleMaybePromise and helps maintain proper types throughout the promise chain.

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: 0

🧹 Nitpick comments (2)
packages/promise-helpers/src/index.ts (2)

10-13: Enhance robustness with optional chaining

The implementation of isActualPromise could be improved by using optional chaining to better handle potential null or undefined values.

export function isActualPromise<T>(value: MaybePromiseLike<T>): value is Promise<T> {
  const maybePromise = value as any;
-  return maybePromise && maybePromise.then && maybePromise.catch && maybePromise.finally;
+  return !!maybePromise?.then && !!maybePromise?.catch && !!maybePromise?.finally;
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


42-58: Remove commented-out code

The file contains a significant block of commented-out code that appears to be an alternative implementation that was considered but not used. This should be removed to improve code readability.

export function fakePromise<T>(value: MaybePromise<T>): Promise<T>;
export function fakePromise<T>(value: MaybePromiseLike<T>): Promise<T>;
export function fakePromise(value: void): Promise<void>;
export function fakePromise<T>(value: MaybePromiseLike<T>): Promise<T> {
-  // if (value && isActualPromise(value)) {
-  //   return value;
-  // }
-
-  // if (isPromise(value)) {
-  //   return {
-  //     then: (resolve, reject) => fakePromise(value.then(resolve, reject)),
-  //     catch: reject => fakePromise(value.then(res => res, reject)),
-  //     finally: cb => fakePromise(cb ? promiseLikeFinally(value, cb) : value),
-  //     [Symbol.toStringTag]: 'Promise',
-  //   };
-  // }
-
🧰 Tools
🪛 Biome (1.9.4)

[error] 44-44: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 06310a2 and 7ee193a.

📒 Files selected for processing (1)
  • packages/promise-helpers/src/index.ts (4 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/promise-helpers/src/index.ts

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 44-44: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 66-66: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 171-171: Do not add then to an object.

(lint/suspicious/noThenProperty)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: e2e / azure-function
  • GitHub Check: e2e / aws-lambda
  • GitHub Check: alpha / snapshot
  • GitHub Check: server (undici)
  • GitHub Check: server (ponyfill)
  • GitHub Check: node-fetch (consumeBody)
  • GitHub Check: server (native)
  • GitHub Check: node-fetch (noConsumeBody)
🔇 Additional comments (6)
packages/promise-helpers/src/index.ts (6)

15-37: Good addition of finallyFactory parameter

The addition of the finallyFactory parameter to the handleMaybePromise function aligns well with the standard Promise API, allowing for cleanup operations regardless of promise resolution status.


33-39: Approve elegant implementation changes

The new implementation is more concise and effectively leverages the Promise API. The use of fakePromise() as a starting point, combined with proper handling of the finallyFactory, results in cleaner code.


165-204: Approve improved error handling in fakeRejectPromise

The enhanced error handling in fakeRejectPromise makes the function more resilient. The implementation now correctly propagates errors and maintains Promise behavior consistency.

🧰 Tools
🪛 Biome (1.9.4)

[error] 171-171: Do not add then to an object.

(lint/suspicious/noThenProperty)


327-351: Well-implemented promiseLikeFinally utility function

The new promiseLikeFinally function elegantly handles both standard Promises with native finally support and PromiseLike objects without it. The implementation correctly preserves the Promise chain behavior.


353-363: Approve the new unfakePromise utility

The unfakePromise function effectively "unwraps" fake Promises, maintaining synchronous behavior where possible and avoiding unnecessary Promise wrapping. This helps optimize performance by reducing unnecessary asynchronous operations.


59-94:

✅ Verification successful

Approve improved error handling in fakePromise

The implementation now correctly handles errors that might occur during promise resolution by using try-catch blocks in the then and finally methods. This makes the function more robust.


🏁 Script executed:

#!/bin/bash
# Check for any instances where fakePromise is used in the codebase to make sure
# the new error handling doesn't introduce regressions

rg -A 3 "fakePromise\(" --type ts

Length of output: 15336


Robust fakePromise Error Handling Verified – No Regressions Detected

The updated implementation in packages/promise-helpers/src/index.ts now wraps the resolution logic in both the then and finally methods with try-catch blocks, ensuring that any errors are properly caught and redirected via fakeRejectPromise. A repository-wide search for fakePromise usage confirms that this change is isolated and does not introduce regressions in other modules.

  • The try-catch in then ensures that any error during resolution is handled.
  • The try-catch in finally appropriately wraps callback execution and chains resolution.

Overall, the improvements enhance robustness without affecting downstream usage.

🧰 Tools
🪛 Biome (1.9.4)

[error] 66-66: Do not add then to an object.

(lint/suspicious/noThenProperty)

@EmrysMyrddin EmrysMyrddin force-pushed the feature/promise-helpers/handle-maybe-promise-finnally branch from 7ee193a to f1b9fcc Compare March 9, 2025 21:46
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: 0

🧹 Nitpick comments (1)
packages/promise-helpers/src/index.ts (1)

10-13: Use optional chaining for safer object property access

The isActualPromise function correctly checks for all required Promise methods, but could be more robust against null or undefined values by using optional chaining.

export function isActualPromise<T>(value: MaybePromiseLike<T>): value is Promise<T> {
  const maybePromise = value as any;
-  return maybePromise && maybePromise.then && maybePromise.catch && maybePromise.finally;
+  return !!maybePromise?.then && !!maybePromise?.catch && !!maybePromise?.finally;
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ee193a and f1b9fcc.

📒 Files selected for processing (1)
  • packages/promise-helpers/src/index.ts (4 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/promise-helpers/src/index.ts

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 44-44: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 52-52: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 62-62: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 163-163: Do not add then to an object.

(lint/suspicious/noThenProperty)

⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: server (undici)
  • GitHub Check: node-fetch (consumeBody)
  • GitHub Check: e2e / cloudflare-workers
  • GitHub Check: e2e / azure-function
  • GitHub Check: server (ponyfill)
  • GitHub Check: alpha / snapshot
  • GitHub Check: e2e / aws-lambda
  • GitHub Check: node-fetch (noConsumeBody)
  • GitHub Check: server (native)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
packages/promise-helpers/src/index.ts (5)

15-40: Good implementation of the finallyFactory parameter

The updated handleMaybePromise function now properly supports a finalization step through the finallyFactory parameter. The refactored implementation effectively chains promises and handles the finally callback regardless of whether the promise resolves or rejects.


42-91: Enhanced fakePromise now handles both promise types and error cases

The updated fakePromise function is more robust and versatile. It now:

  1. Returns actual Promise objects directly
  2. Creates a proper promise wrapper for PromiseLike objects
  3. Includes proper error handling with try/catch blocks

This aligns with the PR objectives to make promise helpers safer against exceptions.

🧰 Tools
🪛 Biome (1.9.4)

[error] 44-44: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 52-52: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 62-62: Do not add then to an object.

(lint/suspicious/noThenProperty)


161-196: Revised fakeRejectPromise implementation

The updated function signature and implementation are now more consistent with the intended use. The commented-out code indicates the deliberate removal of promise forwarding in the then method, which aligns with the PR objectives.

Error handling has been improved throughout the implementation to catch exceptions and maintain the rejected state.

🧰 Tools
🪛 Biome (1.9.4)

[error] 163-163: Do not add then to an object.

(lint/suspicious/noThenProperty)


319-343: Well-implemented promiseLikeFinally polyfill

The new promiseLikeFinally function correctly implements the finally functionality for PromiseLike objects that might not support it natively. It properly handles both success and error cases, ensuring the finalization callback is always executed while preserving the original result or error.


345-355: Clean implementation of unfakePromise

The new unfakePromise function completes the abstraction by converting fake promises back to their original values or throwing stored errors. This creates a clean, symmetrical API for working with promises throughout the library.

@EmrysMyrddin EmrysMyrddin force-pushed the feature/promise-helpers/handle-maybe-promise-finnally branch from f1b9fcc to c964a81 Compare March 9, 2025 21:56
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: 0

🧹 Nitpick comments (2)
packages/promise-helpers/src/index.ts (2)

12-15: Use optional chaining for safer property access.

The isActualPromise function should use optional chaining to safely check for promise-like properties.

export function isActualPromise<T>(value: MaybePromiseLike<T>): value is Promise<T> {
  const maybePromise = value as any;
-  return maybePromise && maybePromise.then && maybePromise.catch && maybePromise.finally;
+  return maybePromise?.then && maybePromise?.catch && maybePromise?.finally;
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


44-94: Enhanced fakePromise with better error handling and type support.

The implementation of fakePromise has been significantly improved:

  1. It now properly handles both MaybePromise<T> and MaybePromiseLike<T> types
  2. It includes proper error handling with try/catch blocks for exception safety
  3. It properly implements the Promise interface including the finally method
  4. It uses the Symbol for identification

The static analysis tool flagged line 46 with "void is confusing outside a return type or a type parameter." Consider using undefined instead.

export function fakePromise<T>(value: MaybePromise<T>): Promise<T>;
export function fakePromise<T>(value: MaybePromiseLike<T>): Promise<T>;
-export function fakePromise(value: void): Promise<void>;
+export function fakePromise(value: undefined): Promise<void>;
export function fakePromise<T>(value: MaybePromiseLike<T>): Promise<T> {
🧰 Tools
🪛 Biome (1.9.4)

[error] 46-46: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 54-54: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 64-64: Do not add then to an object.

(lint/suspicious/noThenProperty)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1b9fcc and 2be6a2a.

📒 Files selected for processing (2)
  • packages/promise-helpers/src/index.ts (4 hunks)
  • packages/promise-helpers/tests/handleMaybePromise.spec.ts (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/promise-helpers/src/index.ts

[error] 14-14: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 46-46: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 54-54: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 64-64: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 166-166: Do not add then to an object.

(lint/suspicious/noThenProperty)

⏰ Context from checks skipped due to timeout of 90000ms (14)
  • GitHub Check: unit / node 23
  • GitHub Check: unit / node 22
  • GitHub Check: unit / deno
  • GitHub Check: unit / bun
  • GitHub Check: unit / node 20
  • GitHub Check: unit / node 18
  • GitHub Check: type check
  • GitHub Check: server (undici)
  • GitHub Check: lint
  • GitHub Check: alpha / snapshot
  • GitHub Check: node-fetch (consumeBody)
  • GitHub Check: server (ponyfill)
  • GitHub Check: node-fetch (noConsumeBody)
  • GitHub Check: server (native)
🔇 Additional comments (7)
packages/promise-helpers/tests/handleMaybePromise.spec.ts (2)

1-3: Good choice of imports from Jest globals.

The test file correctly imports Jest testing utilities from '@jest/globals' instead of from 'node:test', which aligns with the Jest testing framework being used.


5-291: Comprehensive test coverage for the new finally callback functionality.

The test suite thoroughly covers:

  • Synchronous and asynchronous scenarios
  • Success and error cases
  • Promise, sync function, and fake promise handling
  • Chaining behavior with different combinations
  • Error propagation
  • Proper validation that finally callback is always called

This level of test coverage will effectively prevent regressions and ensure the stability of the new functionality.

packages/promise-helpers/src/index.ts (5)

4-5: Good practice using a symbol constant for fake promise identification.

Creating a named constant for the symbol improves maintainability and prevents magic strings.


17-42: Elegant implementation of the finally callback.

The overloaded signatures and implementation of handleMaybePromise now properly support the finallyFactory parameter. The implementation is clean and straightforward:

  1. It creates a fake promise chain with the input, success, and error factories
  2. It conditionally applies the finallyFactory if provided
  3. It returns the unwrapped result using unfakePromise

This is a well-designed enhancement to the function's capabilities.


164-199: Enhanced fakeRejectPromise with better error handling.

The implementation of fakeRejectPromise has been improved with proper error handling in try/catch blocks, making it more robust against exceptions. It now also uses the Symbol-based identification approach.

The static analysis warnings about "Do not add then to an object" can be safely ignored in this case, as creating objects with then methods is the intended behavior for fake promises.

🧰 Tools
🪛 Biome (1.9.4)

[error] 166-166: Do not add then to an object.

(lint/suspicious/noThenProperty)


323-347: Useful promiseLikeFinally utility for non-standard promises.

This is a well-implemented utility function that adds .finally() functionality for PromiseLike objects that don't have it natively. It handles both the success and error paths correctly, ensuring the finally callback is always executed.


349-359: Clean implementation of unfakePromise for unwrapping fake promises.

The unfakePromise function is straightforward and effectively handles both fake resolved promises and fake rejected promises. It's a useful utility that simplifies the rest of the codebase by centralizing the fake promise unwrapping logic.

@ardatan ardatan force-pushed the feature/promise-helpers/handle-maybe-promise-finnally branch from 2be6a2a to d44bf68 Compare March 10, 2025 15:09
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: 0

🧹 Nitpick comments (1)
packages/promise-helpers/src/index.ts (1)

12-15: Consider using optional chaining for more robust type checking.

The function correctly checks for the presence of Promise methods, but could be improved with optional chaining.

-  return maybePromise && maybePromise.then && maybePromise.catch && maybePromise.finally;
+  return maybePromise?.then && maybePromise?.catch && maybePromise?.finally;
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2be6a2a and d44bf68.

📒 Files selected for processing (3)
  • .changeset/late-flies-bet.md (1 hunks)
  • packages/promise-helpers/src/index.ts (4 hunks)
  • packages/promise-helpers/tests/handleMaybePromise.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/promise-helpers/tests/handleMaybePromise.spec.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/promise-helpers/src/index.ts

[error] 14-14: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 46-46: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 54-54: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 64-64: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 166-166: Do not add then to an object.

(lint/suspicious/noThenProperty)

🪛 LanguageTool
.changeset/late-flies-bet.md

[grammar] ~5-~5: You used an adverb (‘finally’) instead of an adjective, or a noun (‘callback’) instead of another adjective.
Context: ...se-helpers': minor --- Allow to pass a finally callback to handleMaybePromise

(A_RB_NN)

⏰ Context from checks skipped due to timeout of 90000ms (17)
  • GitHub Check: unit / deno
  • GitHub Check: unit / node 23
  • GitHub Check: unit / node 22
  • GitHub Check: unit / node 20
  • GitHub Check: unit / bun
  • GitHub Check: unit / node 18
  • GitHub Check: type check
  • GitHub Check: node-fetch (consumeBody)
  • GitHub Check: prettier
  • GitHub Check: server (undici)
  • GitHub Check: lint
  • GitHub Check: node-fetch (noConsumeBody)
  • GitHub Check: esm
  • GitHub Check: alpha / snapshot
  • GitHub Check: server (ponyfill)
  • GitHub Check: server (native)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (9)
packages/promise-helpers/src/index.ts (8)

4-5: Good addition of a constant for the symbol name.

Using a constant for the Symbol name improves maintainability and ensures consistency throughout the codebase.


17-34: Successfully added the finallyFactory parameter to all overloads.

The function signatures have been correctly updated to include the finallyFactory parameter, maintaining consistency across all overloads.


35-42: Improved implementation with better promise chaining.

The refactored implementation is more elegant, using fakePromise() as a starting point for the chain and properly applying the finallyFactory when provided.


44-60: Enhanced type support and early promise detection.

The function now accepts more flexible types and properly handles actual Promise objects early. The check for isActualPromise before isPromise creates a clear hierarchy of promise recognition.

🧰 Tools
🪛 Biome (1.9.4)

[error] 46-46: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 54-54: Do not add then to an object.

(lint/suspicious/noThenProperty)


61-94: Added robust error handling to prevent uncaught exceptions.

The implementation now properly catches exceptions that might occur when executing the resolution or finally callbacks, enhancing reliability.

🧰 Tools
🪛 Biome (1.9.4)

[error] 64-64: Do not add then to an object.

(lint/suspicious/noThenProperty)


164-199: Improved fakeRejectPromise type and error handling.

The function now:

  1. Returns Promise<T> instead of Promise<never>, allowing for more versatile error handling
  2. Includes proper try-catch blocks around callback executions
  3. Uses consistent symbol identification

These improvements make the function more robust and align with the other changes.

🧰 Tools
🪛 Biome (1.9.4)

[error] 166-166: Do not add then to an object.

(lint/suspicious/noThenProperty)


323-347: Well-implemented finally callback for PromiseLike objects.

This function fills an important gap by implementing the finally functionality for PromiseLike objects that may not have a built-in finally method. The implementation correctly handles both synchronous and asynchronous finally callbacks.


349-359: Good addition of unfakePromise for type safety.

This function cleanly converts fake promises back to their original values, preserving the expected return type from handleMaybePromise. The implementation properly handles both resolved and rejected fake promises.

.changeset/late-flies-bet.md (1)

1-6: Appropriate changeset for a minor version bump.

The changeset correctly identifies this as a minor feature addition that adds new functionality without breaking existing code.

🧰 Tools
🪛 LanguageTool

[grammar] ~5-~5: You used an adverb (‘finally’) instead of an adjective, or a noun (‘callback’) instead of another adjective.
Context: ...se-helpers': minor --- Allow to pass a finally callback to handleMaybePromise

(A_RB_NN)

@ardatan ardatan force-pushed the feature/promise-helpers/handle-maybe-promise-finnally branch from fe5f72e to 1eca918 Compare March 13, 2025 14:39
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: 0

🧹 Nitpick comments (2)
packages/promise-helpers/src/index.ts (1)

12-15: Consider using optional chaining for safer property access.

The current implementation might throw if maybePromise is null or undefined. Consider using optional chaining to make the code more robust.

-  return maybePromise && maybePromise.then && maybePromise.catch && maybePromise.finally;
+  return !!maybePromise?.then?.catch?.finally;
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

.changeset/late-flies-bet.md (1)

1-6: Clear and concise changeset description.

The changeset correctly describes the new feature of adding a finally callback to handleMaybePromise. Consider rephrasing slightly for better grammar: "Allow passing a finally callback to handleMaybePromise".

🧰 Tools
🪛 LanguageTool

[grammar] ~5-~5: You used an adverb (‘finally’) instead of an adjective, or a noun (‘callback’) instead of another adjective.
Context: ...se-helpers': minor --- Allow to pass a finally callback to handleMaybePromise

(A_RB_NN)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe5f72e and 1eca918.

📒 Files selected for processing (3)
  • .changeset/late-flies-bet.md (1 hunks)
  • packages/promise-helpers/src/index.ts (4 hunks)
  • packages/promise-helpers/tests/handleMaybePromise.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/promise-helpers/tests/handleMaybePromise.spec.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/promise-helpers/src/index.ts

[error] 14-14: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 46-46: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 54-54: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 64-64: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 166-166: Do not add then to an object.

(lint/suspicious/noThenProperty)

🪛 LanguageTool
.changeset/late-flies-bet.md

[grammar] ~5-~5: You used an adverb (‘finally’) instead of an adjective, or a noun (‘callback’) instead of another adjective.
Context: ...se-helpers': minor --- Allow to pass a finally callback to handleMaybePromise

(A_RB_NN)

⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: lint
  • GitHub Check: prettier
  • GitHub Check: esm
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (9)
packages/promise-helpers/src/index.ts (9)

4-4: Good addition of a symbol name constant.

Using a symbol with a meaningful name helps standardize the identification of fake promises across the codebase.


17-42: Well-structured implementation of the finally callback.

The addition of the finallyFactory parameter to all overloads of handleMaybePromise is consistent and provides a useful way to execute code after the promise settles, regardless of its outcome. The implementation is concise and correctly applies the finallyFactory only when provided.


44-47: Type enhancement for fakePromise function signatures.

The function signatures have been improved to accept both MaybePromise<T> and MaybePromiseLike<T>, enhancing type safety and flexibility.

🧰 Tools
🪛 Biome (1.9.4)

[error] 46-46: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


48-59: Good improvement in handling real promises and promise-like objects.

The revised implementation properly differentiates between actual promises and promise-like objects, with separate handling paths for each. The chaining through fakePromise ensures consistent behavior.

🧰 Tools
🪛 Biome (1.9.4)

[error] 54-54: Do not add then to an object.

(lint/suspicious/noThenProperty)


64-93: Enhanced error handling in the fake promise implementation.

The addition of try-catch blocks in the then and finally methods ensures that exceptions are properly caught and converted to rejected promises, making the implementation more robust.

🧰 Tools
🪛 Biome (1.9.4)

[error] 64-64: Do not add then to an object.

(lint/suspicious/noThenProperty)


164-199: Improved error handling in fakeRejectPromise.

The enhanced implementation now properly catches exceptions in both then and catch methods, ensuring they are correctly converted to rejected promises. This makes the entire promise handling system more resilient to unexpected errors.

🧰 Tools
🪛 Biome (1.9.4)

[error] 166-166: Do not add then to an object.

(lint/suspicious/noThenProperty)


316-321: Updated symbol-based identification for fake promises.

The implementation now uses the standardized symbol to identify fake resolved and rejected promises, which is more maintainable than using string literals directly.


323-347: Well-implemented promiseLikeFinally function.

This utility function provides a clean implementation of the finally functionality for promise-like objects that may not have a native finally method. The implementation correctly preserves both successful results and errors after the finally callback executes.


349-359: Clean implementation of unfakePromise.

The function properly unwraps fake promises to either return their value or throw their stored error. This approach maintains the expected Promise semantics while allowing for synchronous execution when possible.

@ardatan ardatan force-pushed the feature/promise-helpers/handle-maybe-promise-finnally branch from 1eca918 to 19f8ff9 Compare March 13, 2025 14:51
@EmrysMyrddin EmrysMyrddin force-pushed the feature/promise-helpers/handle-maybe-promise-finnally branch from bc59d11 to 938cc7e Compare March 17, 2025 14:29
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: 0

♻️ Duplicate comments (1)
deno-jest.ts (1)

4-13: ⚠️ Potential issue

The it.each implementation has potential issues.

While the parameterized testing implementation works for basic cases, there are several concerns:

  1. The function returns from within the loop which might cause only the last test case to be registered if multiple cases are provided.
  2. No error handling if a placeholder in the test name doesn't exist in the case object.
  3. Type safety could be improved beyond generic object[].

Apply this improved implementation:

it.each =
  (cases: object[]): typeof it =>
  (name, runner) => {
+   // Store all test registrations to ensure all cases run
+   const testResults = [];
    for (const c of cases) {
+     let testName = name;
      Object.entries(c).forEach(([k, v]) => {
-       name = name.replaceAll(k, v);
+       testName = testName.replaceAll(k, String(v));
      });
-     return it(name, runner);
+     testResults.push(it(testName, runner));
    }
+   return testResults[testResults.length - 1] || it(name, () => { throw new Error('No test cases were executed') });
  };
🧹 Nitpick comments (1)
packages/promise-helpers/src/index.ts (1)

12-15: Consider using optional chaining for better null safety.

The function correctly checks for a "real" Promise by verifying the existence of all three Promise methods, but it could benefit from optional chaining.

-  return maybePromise && maybePromise.then && maybePromise.catch && maybePromise.finally;
+  return maybePromise?.then && maybePromise?.catch && maybePromise?.finally;
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc59d11 and 938cc7e.

📒 Files selected for processing (4)
  • .changeset/late-flies-bet.md (1 hunks)
  • deno-jest.ts (1 hunks)
  • packages/promise-helpers/src/index.ts (4 hunks)
  • packages/promise-helpers/tests/handleMaybePromise.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/promise-helpers/tests/handleMaybePromise.spec.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/promise-helpers/src/index.ts

[error] 14-14: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 46-46: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 54-54: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 64-64: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 168-168: Do not add then to an object.

(lint/suspicious/noThenProperty)

🪛 LanguageTool
.changeset/late-flies-bet.md

[grammar] ~5-~5: You used an adverb (‘finally’) instead of an adjective, or a noun (‘callback’) instead of another adjective.
Context: ...se-helpers': minor --- Allow to pass a finally callback to handleMaybePromise

(A_RB_NN)

⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: unit / node 23
  • GitHub Check: unit / node 22
  • GitHub Check: unit / deno
  • GitHub Check: unit / node 20
  • GitHub Check: e2e / cloudflare-modules
  • GitHub Check: unit / bun
  • GitHub Check: unit / node 18
  • GitHub Check: e2e / azure-function
  • GitHub Check: e2e / aws-lambda
🔇 Additional comments (9)
packages/promise-helpers/src/index.ts (7)

4-5: Good addition of centralized symbol name constant.

Using a constant for the symbol name is a good practice to prevent string duplication and ensure consistency across the codebase.


17-42: Great improvement to the handleMaybePromise implementation.

The function has been enhanced to support a finallyFactory parameter, and the implementation has been elegantly simplified using the fakePromise/unfakePromise pattern. This creates a more maintainable and readable code structure.


44-94: Improved fakePromise implementation with better type handling and error management.

The updated implementation:

  1. Properly handles different types of inputs with appropriate type signatures
  2. Adds proper error handling for exceptional cases
  3. Better matches the behavior of real Promises
  4. Uses the centralized symbol name for identification

These changes make the function more robust and consistent with the Promise specification.

🧰 Tools
🪛 Biome (1.9.4)

[error] 46-46: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 54-54: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 64-64: Do not add then to an object.

(lint/suspicious/noThenProperty)


166-201: Enhanced fakeRejectPromise with better error handling.

The changes to fakeRejectPromise improve error handling by safely catching exceptions during reject callbacks. The generic type parameter T instead of never provides more flexibility in how the function can be used.

🧰 Tools
🪛 Biome (1.9.4)

[error] 168-168: Do not add then to an object.

(lint/suspicious/noThenProperty)


317-323: Switched to using the centralized symbol name.

Good improvement to use the newly introduced constant for symbol identification, which ensures consistency throughout the codebase.


325-349: Well-implemented promiseLikeFinally function.

This function adds finally support for PromiseLike objects that don't natively support it, carefully handling both success and error cases and properly chaining the promises. The implementation correctly ensures that the finally callback executes regardless of resolution status.


351-361: Good addition of unfakePromise unwrapping function.

This function cleanly extracts values from fake promises, either returning the value directly or throwing the error, which makes the handleMaybePromise implementation much cleaner.

.changeset/late-flies-bet.md (1)

1-6: Clear and concise changeset description.

The changeset correctly identifies this as a minor version update, which is appropriate for adding a new feature that doesn't break existing functionality. The description clearly communicates the enhancement.

🧰 Tools
🪛 LanguageTool

[grammar] ~5-~5: You used an adverb (‘finally’) instead of an adjective, or a noun (‘callback’) instead of another adjective.
Context: ...se-helpers': minor --- Allow to pass a finally callback to handleMaybePromise

(A_RB_NN)

deno-jest.ts (1)

14-16: The export reorganization looks good.

Separating the it export from the others makes sense now that you've added the it.each method, as it allows for a cleaner organization of exports.

@dotansimha dotansimha requested review from enisdenjo and ardatan March 17, 2025 14:52
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: 0

🧹 Nitpick comments (4)
packages/promise-helpers/tests/handleMaybePromise.spec.ts (4)

67-102: Consider splitting falsy value tests for better isolation.

While the tests are correct, having multiple assertions in a single test makes it harder to identify which specific falsy value might cause a failure. Consider splitting into separate test cases for each falsy value type.

-  it.each(cases)('when fake value is falsy', ({ input, output }) => {
+  it.each(cases)('when fake value is undefined', ({ input, output }) => {
     expect(
       handleMaybePromise(
         () => (input === 'fake' ? fakePromise(undefined) : undefined),
         res => (output === 'fake' ? fakePromise(undefined) : res),
       ),
     ).toBe(undefined);
+  });
+  
+  it.each(cases)('when fake value is null', ({ input, output }) => {
     expect(
       handleMaybePromise(
         () => (input === 'fake' ? fakePromise(null) : null),
         res => (output === 'fake' ? fakePromise(null) : res),
       ),
     ).toBe(null);
+  });

125-132: There's a discrepancy in the test description.

The test description mentions "fake promise" but the test is using a real Promise.reject.

-  it('should call finally and allow chaining with a fake promise', async () => {
+  it('should call finally and allow chaining with a rejected promise', async () => {

191-203: Consider using async/await pattern for consistency.

This test uses try/catch while other tests use the async/await pattern with expect().rejects. Consider using the same pattern for consistency.

-  it('should call finally and allow throw if no error handler is given', async () => {
-    const throwingFn = () => {
-      throw 'error';
-    };
-    try {
-      handleMaybePromise(throwingFn, onSuccess, undefined, onFinally);
-      throw 'Error was not thrown';
-    } catch (err) {
-      expect(err).toBe('error');
-    }
-    expect(onFinally).toHaveBeenCalledTimes(1);
-    expect(onSuccess).not.toHaveBeenCalled();
+  it('should call finally and allow throw if no error handler is given', () => {
+    const throwingFn = () => {
+      throw 'error';
+    };
+    expect(() => {
+      handleMaybePromise(throwingFn, onSuccess, undefined, onFinally);
+    }).toThrow('error');
+    expect(onFinally).toHaveBeenCalledTimes(1);
+    expect(onSuccess).not.toHaveBeenCalled();
   });

259-268: Consider using expect().toThrow for consistency.

Similar to the previous comment, consider using expect().toThrow for consistency with other tests.

-  it('should call finally and allow throw if no error handler is given', async () => {
-    try {
-      handleMaybePromise(() => fakeRejectPromise('error'), onSuccess, undefined, onFinally);
-      throw 'Error has not been thrown';
-    } catch (err) {
-      expect(err).toBe('error');
-    }
-    expect(onFinally).toHaveBeenCalledTimes(1);
-    expect(onSuccess).not.toHaveBeenCalled();
+  it('should call finally and allow throw if no error handler is given', () => {
+    expect(() => {
+      handleMaybePromise(() => fakeRejectPromise('error'), onSuccess, undefined, onFinally);
+    }).toThrow('error');
+    expect(onFinally).toHaveBeenCalledTimes(1);
+    expect(onSuccess).not.toHaveBeenCalled();
   });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2553435 and 4034df8.

📒 Files selected for processing (1)
  • packages/promise-helpers/tests/handleMaybePromise.spec.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: unit / node 23
  • GitHub Check: unit / node 22
  • GitHub Check: unit / deno
  • GitHub Check: unit / node 20
  • GitHub Check: unit / node 18
  • GitHub Check: node-fetch (consumeBody)
  • GitHub Check: e2e / azure-function
🔇 Additional comments (21)
packages/promise-helpers/tests/handleMaybePromise.spec.ts (21)

1-4: LGTM! Clean import setup.

The file properly sets up ESLint directives and imports necessary testing utilities from '@jest/globals' and the functions under test from the source file.


5-14: Test case structure looks good.

Good organization with nested describe blocks and a well-defined test matrix for different input/output combinations.


15-22: Effective testing of synchronous behavior.

The test correctly verifies that handleMaybePromise returns expected values synchronously when using various combinations of regular values and fake promises.


24-38: Good error handling test.

This test effectively verifies that handleMaybePromise correctly processes errors from both synchronous throws and fake rejected promises.


40-65: Thorough testing of error propagation.

The test matrix approach is excellent for covering multiple scenarios. This section verifies that errors from onSuccess callbacks (both thrown and returned as fake rejected promises) are properly propagated.


104-114: Good setup for promise testing with finally callback.

The test setup properly initializes mock functions that return promises for testing the asynchronous behavior with the finally callback.


116-123: Good test for finally with successful promises.

This test effectively verifies that the finally callback is called after successful promise resolution and that the correct value is returned.


134-140: Good test for rejection propagation.

This test correctly verifies that the finally callback is called even when promises are rejected and no error handler is provided.


142-150: Good error propagation test with finally.

Test effectively verifies that the finally callback is called even when onSuccess rejects.


152-160: Good test for onError rejection with finally.

This test correctly verifies that the finally callback is called even when onError rejects.


163-172: Good setup for synchronous testing.

The test correctly sets up mock functions for testing the synchronous behavior with the finally callback.


174-179: Good test for finally with synchronous success.

This test effectively verifies that the finally callback is called after successful synchronous execution.


181-189: Good test for synchronous error handling with finally.

This test correctly verifies that the finally callback is called even when synchronous functions throw errors.


205-215: Good test for onSuccess throws with finally.

This test effectively verifies that the finally callback is called even when onSuccess throws an error.


217-227: Good test for onError throws with finally.

This test correctly verifies that the finally callback is called even when onError throws an error.


230-239: Good setup for fake promises testing.

The test correctly sets up mock functions that return fake promises for testing.


241-248: Good test for finally with fake promises.

This test effectively verifies that the finally callback is called with fake promises and the correct value is returned.


250-257: Good test for rejection with fake promises.

This test correctly verifies that the finally callback is called with rejected fake promises.


270-278: Good test for onSuccess rejection with fake promises.

This test effectively verifies that the finally callback is called even when onSuccess returns a rejected fake promise.


280-288: Good test for onError rejection with fake promises.

This test correctly verifies that the finally callback is called even when onError returns a rejected fake promise.


289-292: Overall test organization is excellent.

The test suite is well-organized, thorough, and covers all the key scenarios for the new finally callback feature.

@EmrysMyrddin EmrysMyrddin merged commit 54a26bb into master Mar 17, 2025
25 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Apr 8, 2025
4 tasks
@ardatan ardatan deleted the feature/promise-helpers/handle-maybe-promise-finnally branch May 12, 2025 16:19
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.

2 participants