- Notifications
You must be signed in to change notification settings - Fork237
Typesafe error propagation in signal connection path#1747
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
This reverts commitd920fa7.
changeset-botbot commentedNov 14, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
🦋 Changeset detectedLatest commit:90d9eca The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means?Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
github-actionsbot commentedNov 14, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
size-limit report 📦
|
Updated version type for livekit-client from patch to minor.
…dk-js into lukas/neverthrow-signal
| exportclassWebSocketStream<TextendsArrayBuffer|string=ArrayBuffer|string>{ | ||
| readonlyurl:string; | ||
| readonlyopened:Promise<WebSocketConnection<T>>; | ||
| readonlyopened:ResultAsync<WebSocketConnection<T>,WebSocketError>; | ||
| readonlyclosed:Promise<WebSocketCloseInfo>; | ||
| readonlyclosed:ResultAsync<WebSocketCloseInfo,WebSocketError>; | ||
| readonlyclose:(closeInfo?:WebSocketCloseInfo)=>void; | ||
| readonlyclose!:(closeInfo?:WebSocketCloseInfo)=>void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
suggestion: if the plan is to eventually migrate to the in-built browserWebsocketStream (and from what I can see, it is onsome sort ofstandards track), it might be worth treating this like vendored code that shouldn't be modified and doing theseResultAsync patches where this is being used in the signal client code.
I can think of pros and cons to both approaches though so I'd be curious to hear your rationale if there is a reason why doing this would be overly burdensome.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Fair point. Was thinking about this too. I have currently less confidence into adapting the standard proposal any time soon. Given that, I think starting with the error propagation at the lowest level sounded like the right choice to me
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
xianshijing-lk left a comment• edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
some minor comments.
From the general code structure perspective, I think it is the right direction to setup a pattern where the code will throw.
I think moving the throw code to upper layer sounds a good idea, assuming the lower level codestop throwing for expected errors.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
1egoman left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Looks good to me! Just generally, I think I'm realizing there's some fairly nuanced behavior with neverthrow that doesn't carry over 1:1 with how rust handles errors, so I think I'll need to spend some time digging into some of those nuances.
| constself=this; | ||
| consthandleSignalConnected=( | ||
| connection:WebSocketConnection, | ||
| firstMessage?:SignalResponse, | ||
| )=>{ | ||
| this.handleSignalConnected(connection,wsTimeout,firstMessage); | ||
| }; | ||
| returnwithMutex( | ||
| safeTry<U,ConnectionError>(asyncfunction*(){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
suggestion(non-blocking): When I was first trying to parse through this I saw you swapped allthis ->self and it took me a second to figure out exactly whereself was being assigned and why you had done this. It looks like (from what I can tell) it's due to the generator function creating a new explicitthis scope.
I wanted to propose another option instead - something likeasync function* () {}.bind(this). There's some discussion here on the pros/cons:supermacro/neverthrow#632. At least as somebody coming into reading this code for the first time, I think it would help but it's minor and something I don't have a strong opinion on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Generally 50:50 on both, but reading through the issue you linked it mentions two steps being necessary for typescript today to properly bind it, i.e.:return safeTry(function* (this: CurrentClass) {}).bind(this) which would sway me to leave it as is.
Uh oh!
There was an error while loading.Please reload this page.
| reason:unknown, | ||
| validateUrl:string, | ||
| ):Promise<ConnectionError>{ | ||
| ):Promise<Result<never,ConnectionError>>{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
nitpick: I think this should beResultAsync instead and the returns from this function updated to beerrAsync?
UPDATE: reading a little further, this might actually be intended. I'm still leaving this comment though because I think it's relevant to a future comment.
| returnResultAsync.fromPromise(Promise.race(settledPromises),(e)=>easE); | ||
| } | ||
| exporttypeResultAsyncLike<T,E>=ResultAsync<T,E>|Promise<Result<T,E>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
question: Interesting - in mylast comment I had assumed thatPromise<Result<T, E>> was a mistake, but this line implies this is very much intentional. Is this a pattern because of async functions always returning promises, ie, the same type of topic asthis issue?
Just thinking out loud but I wonder if handling this difference at this level of abstraction is the right place to do it (naively as somebody who hasn't really dug too much intoneverthrow in depth yet, this semantic difference is surprising), or if it would be better to write a utility function which can convert anyPromise<Result<T, E>> to aResultAsync<T, E>.
I think I'd need to play around with it a bit on my own to develop a stronger opinion so feel free to keep it how it is for now and we can discuss it in the future once I've been able to dig in further.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
yeah, it's on purpose but mostly as an intermediate step until we have ported all async internals to the result type.
The helper function is a nice idea, we'd then have to have it include a genericError though as thePromise could still through and would have to be unwrapped safely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Is this a pattern because of async functions always returning promises, ie, the same type of topic assupermacro/neverthrow#42?
yes, this was the reason and for the helpers I thought having a helper type to accept both would be nice
| constself=this; | ||
| constrestartResultAsync=safeTry(asyncfunction*(){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
suggestion(non-blocking): another instance ofhttps://github.com/livekit/client-sdk-js/pull/1747/files#r2546082926 - consider maybe also using.bind(this) here.
8b8ddbd intomainUh oh!
There was an error while loading.Please reload this page.
No description provided.