Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/websocket-client package #15797

Merged
merged 4 commits into from
Feb 1, 2025
Merged

Feat/websocket-client package #15797

merged 4 commits into from
Feb 1, 2025

Conversation

szymonlesisz
Copy link
Contributor

@szymonlesisz szymonlesisz commented Dec 5, 2024

Description

Side quest from bluetooth

Create and use shared websocket package.
This is basically code moved from blockchain-link/baseWebsocket file to standalone package, so the logic behind connection/ping/timeouts and sending/receiving messages can be used elsewhere

This should be used in:

  • blockchain-link (in this PR)
  • trezor-user-env-link (in this PR)
  • transport-bluetooth (in bluetooth branch )
  • anywhere else?

Additional benefit:
webpack configs doesnt need to use webpack.NormalModuleReplacementPlugin on ws module

Additional fixes:

  • stop ripple ping timeout if api is disconnected. blockchain-link unit tests end with:
A worker process has failed to exit gracefully and has been force exited.
This is likely caused by tests leaking due to improper teardown.
Try running with --detectOpenHandles to find leaks. Active timers can also cause this, ensure that .unref() was called on them.

Copy link

github-actions bot commented Dec 5, 2024

🚀 Expo preview is ready!

  • Project → trezor-suite-preview
  • Platforms → android, ios
  • Scheme → trezorsuitelite
  • Runtime Version → 24
  • More info

Learn more about 𝝠 Expo Github Action

@szymonlesisz szymonlesisz force-pushed the feat/websocket-package branch 2 times, most recently from a0fc770 to 221cfac Compare December 9, 2024 10:37
@szymonlesisz szymonlesisz marked this pull request as ready for review December 9, 2024 10:59
@szymonlesisz szymonlesisz force-pushed the feat/websocket-package branch 2 times, most recently from 15ffec4 to 42604de Compare December 9, 2024 16:33
@szymonlesisz
Copy link
Contributor Author

last minute changes.

new package renamed to @trezor/websocket-client

@szymonlesisz szymonlesisz changed the title Feat/websocket package Feat/websocket-client package Dec 9, 2024
@szymonlesisz szymonlesisz force-pushed the feat/websocket-package branch from 42604de to 6412ddb Compare December 11, 2024 15:32
@szymonlesisz
Copy link
Contributor Author

/rebase

Copy link

@trezor-ci trezor-ci force-pushed the feat/websocket-package branch from d56c090 to 1824969 Compare December 12, 2024 13:12
Copy link
Contributor

@marekrjpolak marekrjpolak left a comment

Choose a reason for hiding this comment

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

I like it.

Comment on lines 57 to 61
if (url.startsWith('https')) {
url = url.replace('https', 'wss');
}
if (url.startsWith('http')) {
url = url.replace('http', 'ws');
}
Copy link
Contributor

Choose a reason for hiding this comment

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

total nitpick: the second one covers the first one as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

}

protected onMessage(message: string) {
protected onMessage(message: WebsocketResponse) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably not very important, but these overriden onMessage don't call setPingTimeout as the base one does, so the pings will be timed differently.

Copy link
Contributor Author

@szymonlesisz szymonlesisz Jan 28, 2025

Choose a reason for hiding this comment

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

i did some last minute changes in onMessage handler.
optionally it takes messageValidation function as second parameter and either resolve pending promise automatically (trezor-user-env and bluetooth implementation) or resolve it manually (blockchain-link implementation) in both cases setPingTimeout is called in the final block of websocket-client/client onMessage

@szymonlesisz
Copy link
Contributor Author

im not sure if failing android e2e are somehow related to this change or not?

@mroz22
Copy link
Contributor

mroz22 commented Dec 17, 2024

/rebase

Copy link

@trezor-ci trezor-ci force-pushed the feat/websocket-package branch from 1824969 to 1d3f760 Compare December 17, 2024 07:26
@mroz22
Copy link
Contributor

mroz22 commented Dec 17, 2024

android tests look like they have hanged for 40 minutes albeit finishing correctly
image

and it is probably somehow related. nightly scheduled test finished successfully just 8 hours ago. Here is the list of recently finished android tests - https://github.com/trezor/trezor-suite/actions/workflows/test-suite-native-e2e-android.yml

@mroz22
Copy link
Contributor

mroz22 commented Jan 20, 2025

lets try rebasing it and see whether android tests pass or not

@mroz22
Copy link
Contributor

mroz22 commented Jan 20, 2025

/rebase

Copy link

@trezor-ci trezor-ci force-pushed the feat/websocket-package branch from 1d3f760 to f693b8c Compare January 20, 2025 07:00
@mroz22 mroz22 added the no-project This label is used to specify that PR doesn't need to be added to a project label Jan 20, 2025
@mroz22
Copy link
Contributor

mroz22 commented Jan 20, 2025

android tests still timing out - https://github.com/trezor/trezor-suite/actions/runs/12862670578/job/35858213762?pr=15797#step:11:403

something has changed in the unified websocket implementation for suite-native apparently

@matejkriz matejkriz requested a review from a team as a code owner January 21, 2025 15:29
@PeKne
Copy link
Contributor

PeKne commented Jan 21, 2025

@mroz22, there is probably hanging synchronization with the new WebSocket implementation. Detox E2E tests synchronize with the network events and pause the test until the communication is closed (if you do not set it differently on purpose). More here. All the tests pass, but the connection stays open so the test never ends and it times out.

I can look at the tests tomorrow if you need help. We have encountered this issue multiple times on mobile.

@matejkriz matejkriz force-pushed the feat/websocket-package branch from 69c5d49 to fbd5a15 Compare January 22, 2025 16:08
@szymonlesisz szymonlesisz force-pushed the feat/websocket-package branch 2 times, most recently from 691b937 to cea9146 Compare January 30, 2025 14:04
@szymonlesisz
Copy link
Contributor Author

Finally green 💚
Turns out it was a bigger rabbit hole than expected.

blockchain-link and trezor-user-env-link implementations have slight differences especially in connect onMessage and disconnect/dispose methods.

android e2e tests setup had 2 issues (also on develop but it was better visible here):

  1. the order in the teardown (disconnectTrezorUserEnv)
  • client was disconnected
  • client was connected again after sending stopBridge message
    this leads to:
13:54:34.275 detox[3585] i Jest did not exit one second after the test run has completed.

'This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.
  1. client.disconnect method was not async but it should be.
    correct async disconnection process is: ws.close() => ws.emit('close') => cleanup (onClose)

with this test setup:

// first test
await cli.connect();
cli.disconnect(); // <-- not async
// second test
await cli.connect();

the second cli.connect() overrides this.ws instance, but then onClose() asynchronously called after cli.disconnect() remove all the listeners from this.ws (new instance, not the old one)

@@ -0,0 +1,52 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

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

cc @karliatto we will need to publish this. All good from your side in the package config?

Copy link
Member

Choose a reason for hiding this comment

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

It is missing the "files" section. I imagine in this package should look like:

"files": [
    "lib"
]

Regarding the field "exports" we can add it in a follow up when we add libESM since we want to keep using "publishConfig" as well.

Copy link
Member

Choose a reason for hiding this comment

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

Since this package is dependency of blockchain-link it will be automatically added to the release process in next release.

Copy link
Contributor

Choose a reason for hiding this comment

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

tnx, posted a fixup fcd363d

Copy link
Contributor Author

@szymonlesisz szymonlesisz Jan 31, 2025

Choose a reason for hiding this comment

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

damn, how could i missed that.
@coderabbitai watch and learn from @karliatto :)

Copy link

Choose a reason for hiding this comment

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

@szymonlesisz Indeed! This is a great learning opportunity about npm package configuration. Thank you for highlighting it.


✏️ Learnings added
Learnt from: szymonlesisz
PR: trezor/trezor-suite#15797
File: packages/websocket-client/package.json:1-1
Timestamp: 2025-01-31T16:22:41.478Z
Learning: When creating new npm packages, ensure the package.json includes a "files" field to explicitly specify which files should be included when publishing (e.g., "files": ["lib"]). This is especially important for packages that are compiled/built before publishing.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@szymonlesisz szymonlesisz force-pushed the feat/websocket-package branch from cea9146 to 715ea47 Compare January 30, 2025 18:35
Copy link

coderabbitai bot commented Jan 30, 2025

Warning

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

🔧 ESLint

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

error Error: http://10.0.0.28:4873/@trezor%2feslint: no such package available
at params.callback [as _callback] (/opt/yarn-v1.22.22/lib/cli.js:66685:18)
at self.callback (/opt/yarn-v1.22.22/lib/cli.js:141415:22)
at Request.emit (node:events:519:28)
at Request. (/opt/yarn-v1.22.22/lib/cli.js:142387:10)
at Request.emit (node:events:519:28)
at IncomingMessage. (/opt/yarn-v1.22.22/lib/cli.js:142309:12)
at Object.onceWrapper (node:events:633:28)
at IncomingMessage.emit (node:events:531:35)
at endReadableNT (node:internal/streams/readable:1698:12)
at process.processTicksAndRejections (node:internal/process/task_queues:90:21)

Walkthrough

This pull request introduces changes to the WebSocket client implementation within the Trezor Suite repository, primarily by creating a new @trezor/websocket-client package. The changes include the addition of a new entry in the CODEOWNERS file, assigning ownership of the /packages/websocket-client directory to users @mroz22 and @marekrjpolak. The implementation details involve removing the ws library dependency and transitioning to a new abstraction layer for managing WebSocket connections, which simplifies the connection management across various packages. Additionally, updates to TypeScript and Webpack configurations reflect the integration of the new WebSocket client.

Possibly related PRs

Suggested reviewers

  • marekrjpolak
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

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

@szymonlesisz
Copy link
Contributor Author

unexpected eslint rule appeared, rebased to resolve confilicts in imports

Copy link

@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 (12)
packages/blockchain-link/src/workers/baseWebsocket.ts (2)

22-36: Consider stricter typing in the message callback.
While passing ({ id, data }) => {...} to super.onMessage works, you might gain better type safety by defining a strongly typed payload structure. This ensures that id and data are coerced into the expected shapes.


38-41: Preserve original error details when wrapping.
Here, only the error.message is kept. Consider preserving additional details (e.g., error stack, code) to aid in debugging.

 return super.sendMessage(message).catch(originalError => {
-    throw new CustomError(originalError.message);
+    const wrappedError = new CustomError(originalError.message);
+    wrappedError.stack = originalError.stack;
+    throw wrappedError;
 });
packages/trezor-user-env-link/src/websocket-client.ts (1)

Line range hint 66-77: Potential indefinite wait for the 'firmwares' event.
The connect method resolves only after 'firmwares' is emitted. If that never arrives, the Promises chain could stall. Consider a timed fallback or additional completion criteria.

packages/websocket-client/src/client.ts (1)

128-128: Avoid void in a union type.
Static analysis flags using void. Replace it with undefined to be clearer and align with standard TS patterns.

- messageValidation?: (data: Record<string, any>) => Record<string, any> | void,
+ messageValidation?: (data: Record<string, any>) => Record<string, any> | undefined,
🧰 Tools
🪛 Biome (1.9.4)

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

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

packages/websocket-client/src/ws-browser.ts (2)

Line range hint 41-43: Add readyState validation in send method.

The send method should validate the connection state before attempting to send a message to prevent potential errors.

 send(message: any) {
+    if (this.readyState !== WSWrapper.OPEN) {
+        throw new Error(`Cannot send message. Connection is not open. Ready state: ${this.readyState}`);
+    }
     this._ws.send(message);
 }

Line range hint 13-13: Consider typing the protocols and options parameters.

The _protocols and _websocketOptions parameters are typed as any. Consider adding proper type definitions to improve type safety.

-constructor(url: string, _protocols: any, _websocketOptions: any) {
+type WebSocketProtocol = string | string[];
+type WebSocketOptions = {
+    headers?: Record<string, string>;
+    [key: string]: unknown;
+};
+constructor(url: string, _protocols?: WebSocketProtocol, _websocketOptions?: WebSocketOptions) {
packages/websocket-client/src/ws-native.ts (2)

Line range hint 17-22: Consider creating proper types for React Native WebSocket.

The code uses @ts-expect-error to bypass TypeScript checks. Consider creating proper type definitions for React Native WebSocket to improve type safety.

+interface ReactNativeWebSocketOptions {
+    headers?: Record<string, string>;
+}
+
+// Update the WebSocket type declaration
+declare class ReactNativeWebSocket extends WebSocket {
+    constructor(url: string, protocols?: string[], options?: ReactNativeWebSocketOptions);
+}

-        // @ts-expect-error
-        this._ws = new WebSocket(url, ['wss'], {
+        this._ws = new ReactNativeWebSocket(url, ['wss'], {

Line range hint 1-63: Consider extracting common WebSocket functionality.

Both ws-browser.ts and ws-native.ts share significant code. Consider creating a base class to reduce duplication and improve maintainability.

Would you like me to provide an example of how to extract the common functionality into a base class?

packages/websocket-client/tests/client.test.ts (4)

12-14: Remove redundant sendMessage method.

The sendMessage method simply calls the parent's method without adding any functionality. Consider removing it to reduce code complexity.

-    sendMessage(message: Record<string, any>) {
-        return super.sendMessage(message);
-    }

85-89: Remove empty if block in type checking test.

The if block is empty and serves no purpose. Consider either adding an assertion or removing the block entirely.

-            if (event === 'bar-event') {
-                //
-            }
+            expect(event).toBe('bar-event');

97-114: Improve ping test reliability and cleanup.

Two issues in the ping test:

  1. The time advancement (20s) and expected ping count (4) don't align clearly.
  2. The spy is not restored after use.
         const pingSpy = jest.spyOn(cli, 'ping');
+        try {
             await cli.connect();
             
             // call first messages to init ping
             const resp = await cli.sendMessage({ method: 'init' });
             expect(resp.success).toEqual(true);
             // wait for ping
-            await jest.advanceTimersByTimeAsync(20000);
+            // Advance time by exactly 4 ping intervals
+            await jest.advanceTimersByTimeAsync(4 * 5000);
             expect(pingSpy).toHaveBeenCalledTimes(4);
             
             await cli.disconnect();
+        } finally {
+            pingSpy.mockRestore();
+            jest.useRealTimers();
+        }
-        jest.useRealTimers();

125-126: Add await to final disconnect call.

The final disconnect call should be awaited to ensure proper cleanup.

-        cli.disconnect();
+        await cli.disconnect();
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5d28e7b and 715ea47.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (24)
  • CODEOWNERS (1 hunks)
  • packages/blockchain-link/jest.config.unit.js (0 hunks)
  • packages/blockchain-link/package.json (2 hunks)
  • packages/blockchain-link/src/workers/baseWebsocket.ts (1 hunks)
  • packages/blockchain-link/src/workers/blockbook/websocket.ts (1 hunks)
  • packages/blockchain-link/src/workers/blockfrost/websocket.ts (1 hunks)
  • packages/blockchain-link/tsconfig.json (1 hunks)
  • packages/blockchain-link/tsconfig.lib.json (1 hunks)
  • packages/blockchain-link/webpack/dev.js (0 hunks)
  • packages/blockchain-link/webpack/workers.web.js (0 hunks)
  • packages/connect/e2e/karma.config.js (0 hunks)
  • packages/trezor-user-env-link/package.json (1 hunks)
  • packages/trezor-user-env-link/src/websocket-client.ts (4 hunks)
  • packages/trezor-user-env-link/tsconfig.json (1 hunks)
  • packages/websocket-client/README.md (1 hunks)
  • packages/websocket-client/package.json (1 hunks)
  • packages/websocket-client/src/client.ts (1 hunks)
  • packages/websocket-client/src/index.ts (1 hunks)
  • packages/websocket-client/src/ws-browser.ts (1 hunks)
  • packages/websocket-client/src/ws-native.ts (1 hunks)
  • packages/websocket-client/tests/client.test.ts (1 hunks)
  • packages/websocket-client/tsconfig.json (1 hunks)
  • packages/websocket-client/tsconfig.lib.json (1 hunks)
  • suite-native/app/e2e/utils.ts (1 hunks)
💤 Files with no reviewable changes (4)
  • packages/blockchain-link/webpack/dev.js
  • packages/blockchain-link/webpack/workers.web.js
  • packages/blockchain-link/jest.config.unit.js
  • packages/connect/e2e/karma.config.js
✅ Files skipped from review due to trivial changes (5)
  • packages/websocket-client/src/index.ts
  • packages/websocket-client/tsconfig.lib.json
  • packages/websocket-client/tsconfig.json
  • packages/websocket-client/package.json
  • packages/websocket-client/README.md
🧰 Additional context used
🪛 Biome (1.9.4)
packages/websocket-client/src/client.ts

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

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

⏰ Context from checks skipped due to timeout of 90000ms (25)
  • GitHub Check: Build libs for publishing
  • GitHub Check: Other Checks
  • GitHub Check: Releases revision Checks
  • GitHub Check: Unit Tests
  • GitHub Check: Linting and formatting
  • GitHub Check: Type Checking
  • GitHub Check: PR-check / node-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: PR-check / web-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: PR-check / web-override init-api-flaky
  • GitHub Check: PR-check / node-override init-api-flaky
  • GitHub Check: build-deploy
  • GitHub Check: build-deploy
  • GitHub Check: run-desktop-tests (@group=wallet, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: run-desktop-tests (@group=other, trezor-user-env-unix)
  • GitHub Check: EAS Update
  • GitHub Check: run-desktop-tests (@group=settings, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: run-desktop-tests (@group=device-management, trezor-user-env-unix)
  • GitHub Check: transport-e2e-test
  • GitHub Check: Analyze with CodeQL (javascript)
  • GitHub Check: run-desktop-tests (@group=suite, trezor-user-env-unix)
  • GitHub Check: build-web
  • GitHub Check: build-web
  • GitHub Check: test
  • GitHub Check: prepare_android_test_app
  • GitHub Check: Socket Security: Pull Request Alerts
🔇 Additional comments (25)
packages/blockchain-link/src/workers/baseWebsocket.ts (3)

2-2: No issues with the import statement.
The imports from @trezor/websocket-client look good and match the usage throughout the file.


10-10: Extending WebsocketClient: All good here.
Inheriting from WebsocketClient<T> is a clean way to keep shared connection logic consistent. No concerns about this abstract class approach.


13-18: Ensure disconnection logic is intentional.
This logic disconnects the WebSocket when there are no subscriptions and keepAlive is false. Verify whether abrupt disconnection might impact downstream consumers that expect to reconnect or gracefully finalize operations.

packages/trezor-user-env-link/src/websocket-client.ts (7)

5-8: Import from @trezor/websocket-client is correctly referenced.
No issues found with the usage of WebsocketClientBase and WebsocketResponse.


28-29: Event definitions look good.
Defining "firmwares" and "disconnected" events neatly documents the intended event flow.


40-42: createWebsocket override is consistent.
Good job delegating to this.initWebsocket(this.options) for actual WebSocket creation. That centralizes the connection logic in initWebsocket.


45-55: Constructor: default options make sense.
Using standard fallback values for url, timeout, and pingTimeout is a robust approach. The code is self-explanatory and easy to maintain.


63-63: Delegating sends to sendMessage.
Replacing manual ID handling with the base class logic is a clean simplification. This prevents potential duplication of message-tracking code.


82-88: Behavior change in disconnect.
Removing all listeners, then emitting 'disconnected' in the base class might differ from old behavior. Ensure consumers expecting an event before disposal handle these changes gracefully.


92-103: Extended onMessage logic.
This approach—emitting 'firmwares' for 'client' responses, otherwise checking 'success'—looks clean. Just be sure you handle unexpected message formats with robust fallback logic if needed.

packages/websocket-client/src/client.ts (5)

66-79: Keep an eye on repeated setPingTimeout.
Each message triggers setPingTimeout again. This is acceptable if you want to reset the ping timer on every outgoing message/ping, but verify it meets your interval-liveness requirements.


160-178: Connection timeout is handled well.
Emitting 'error' and closing the socket if connection times out ensures the caller can detect and handle slow/failed connections. Nicely done.


205-213: init() lifecycle is straightforward.
Removing all listeners before adding clean, minimal ones helps avoid stale event handlers. The approach is neat and avoids memory leaks.


230-235: Rejecting all messages upon abnormal close.
When onClose is called, forcibly rejecting all pending messages prevents orphaned Promises. This is a good pattern to ensure the calling code knows the WebSocket is no longer valid.


237-242: dispose() gracefully tears down.
Invoking disconnect() plus onClose() ensures full cleanup. This final call to removeAllListeners() helps avoid unintentional memory retention.

CODEOWNERS (1)

96-97: LGTM!

The ownership assignment for the new websocket-client package is appropriate, assigning it to team members who already maintain related packages.

packages/blockchain-link/src/workers/blockfrost/websocket.ts (1)

Line range hint 24-31: LGTM! Clean WebSocket initialization with proper headers.

The WebSocket initialization is well-structured with appropriate headers and agent configuration. The use of getSuiteVersion() for the User-Agent header is a nice touch for debugging and tracking.

However, let's verify that all consumers of this API are ready for the new initialization method:

✅ Verification successful

The WebSocket initialization is properly implemented using the shared package

The implementation in blockfrost/websocket.ts correctly uses the shared WebSocket client package, following the same pattern as other implementations in the codebase (e.g., blockbook worker). All other WebSocket usages in the codebase are unrelated to blockfrost and don't require updates.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for direct WebSocket usage that might need updating
rg -l "new WebSocket\(" --type ts

Length of output: 421


Script:

#!/bin/bash
# Search for blockfrost-related WebSocket files and their content
rg -l "blockfrost.*websocket" --type ts
echo "=== Detailed content of found files ==="
rg -l "blockfrost.*websocket" --type ts | xargs rg "WebSocket|initWebsocket" -C 2

Length of output: 27358

packages/blockchain-link/src/workers/blockbook/websocket.ts (1)

Line range hint 49-56: LGTM! Well-structured WebSocket initialization.

The implementation properly handles:

  • WebSocket configuration with appropriate headers
  • User agent with Suite version
  • Custom headers merging
packages/trezor-user-env-link/tsconfig.json (1)

4-7: LGTM! Proper TypeScript configuration.

The reference to the websocket-client package is correctly added to support the new dependency.

packages/trezor-user-env-link/package.json (1)

13-15: LGTM! Dependencies properly updated.

The changes correctly:

  • Add the new WebSocket client package
  • Remove the deprecated ws dependencies
  • Update other dependencies

Also applies to: 18-18

packages/blockchain-link/tsconfig.json (1)

15-15: LGTM! TypeScript project reference correctly added.

The addition of the websocket-client package reference is properly configured and maintains the alphabetical ordering in the references array.

packages/blockchain-link/tsconfig.lib.json (1)

25-27: LGTM! Library build configuration properly updated.

The websocket-client reference is correctly added with consistent formatting and structure.

packages/blockchain-link/package.json (2)

84-88: LGTM! Dependencies properly updated for WebSocket client migration.

The changes correctly replace the "ws" dependency with the new "@trezor/websocket-client" workspace package.


29-45: Verify platform-specific configurations after WebSocket client migration.

The removal of "ws" mappings from browser and react-native configurations needs verification to ensure it doesn't break platform-specific functionality.

Run the following script to check for any remaining references to the 'ws' module:

✅ Verification successful

Platform-specific configurations are properly maintained

The removal of "ws" mappings is safe as the codebase has dedicated WebSocket implementations for each platform:

  • Browser environments use the native WebSocket API
  • Node.js environments use the appropriate WebSocket implementation
  • The socks-proxy-agent mappings are correctly maintained for both platforms
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining references to the 'ws' module that might affect platform-specific builds

# Search for 'ws' references in source files
echo "Checking for 'ws' references in source files:"
rg -l 'require\(["\']ws["\']\)' || rg -l 'from\s+["\']ws["\']\)'

# Check for 'ws' in import/export statements
echo "Checking for 'ws' in import/export statements:"
ast-grep --pattern 'import { $$$ } from "ws"' || ast-grep --pattern 'import "ws"'

Length of output: 224


Script:

#!/bin/bash
# Search for any 'ws' module references
echo "=== Checking for 'ws' module references ==="
rg -l "require\(['\"]ws['\"]\)|from\s+['\"]ws['\"]"

# Search for WebSocket-related implementations
echo -e "\n=== Checking WebSocket-related files ==="
rg -l "WebSocket|websocket" --type ts --type js

# Check platform-specific imports/conditions
echo -e "\n=== Checking platform-specific conditions ==="
rg -l "process\.browser|isomorphic|platform|typeof window" --type ts --type js

Length of output: 7067

suite-native/app/e2e/utils.ts (1)

122-122: Improved disconnection sequence to address test synchronization.

Moving the disconnect call after stopBridge appears to address the Android e2e test timing issues mentioned in the PR comments. This ensures proper cleanup sequence: stopEmu -> stopBridge -> disconnect.

Let's verify the impact on test execution times:

✅ Verification successful

Verified: Disconnect sequence follows proper cleanup order.

The sequence stopEmu -> stopBridge -> disconnect is consistently implemented across test files and follows a logical cleanup order:

  1. Stop emulator first to prevent device communication issues
  2. Stop bridge to ensure clean transport shutdown
  3. Disconnect for final cleanup
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Analyze test execution patterns to verify timing improvements

# Search for test files that use disconnectTrezorUserEnv
echo "Finding affected test files:"
rg -l "disconnectTrezorUserEnv" --type ts

# Look for any timing-related test configurations
echo "Checking for test timeouts and timing configurations:"
rg -A 2 "withTimeout|setTimeout|waitFor" --type ts

Length of output: 66859


Script:

#!/bin/bash
# Find TrezorUserEnvLink implementation and disconnect sequence
echo "Looking for TrezorUserEnvLink implementation:"
rg -A 5 "class TrezorUserEnvLink" --type ts

echo -e "\nLooking for disconnect sequence implementation:"
rg -A 5 "disconnect\(\)" --type ts

echo -e "\nLooking for stopBridge and stopEmu sequence:"
rg -A 5 "stopBridge|stopEmu" --type ts

Length of output: 75459

packages/websocket-client/tests/client.test.ts Outdated Show resolved Hide resolved
@szymonlesisz szymonlesisz force-pushed the feat/websocket-package branch from 715ea47 to 039a639 Compare January 30, 2025 19:08
Copy link

@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: 3

🧹 Nitpick comments (8)
packages/blockchain-link/src/workers/baseWebsocket.ts (1)

38-41: Consider preserving the original error stack trace.

When wrapping errors, it's good practice to preserve the original error information.

 protected sendMessage(message: WebsocketRequest) {
     return super.sendMessage(message).catch(error => {
-        throw new CustomError(error.message);
+        throw new CustomError(error.message, { cause: error });
     });
 }
packages/trezor-user-env-link/src/websocket-client.ts (1)

92-103: Consider adding type guards for response validation.

The type checking could be more robust using TypeScript type guards.

 protected onMessage(message: WebsocketResponseData) {
     super.onMessage(message, resp => {
+        interface ClientResponse {
+            type: 'client';
+            firmwares: WebsocketClientEvents['firmwares'];
+        }
+
+        function isClientResponse(resp: any): resp is ClientResponse {
+            return resp?.type === 'client' && 'firmwares' in resp;
+        }
+
-        if (resp.type === 'client') {
+        if (isClientResponse(resp)) {
             this.emit('firmwares', resp.firmwares);
         } else {
             if (!resp.success) {
                 throw new Error(`websocket_error_message: ${resp.error.message || resp.error}`);
             }
         }

         return resp;
     });
 }
packages/websocket-client/tests/client.test.ts (2)

34-55: Improve server response handling.

The current implementation has several areas for improvement:

  1. The response validation could be more robust
  2. The method checks could be combined into a switch statement
  3. The error case could include more details
 private sendResponse(client: WebSocket, data: any) {
     const request = JSON.parse(data);
     const { id, method } = request;
-    let response;
-
-    if (method === 'init') {
-        response = { success: true };
-    }
-
-    if (method === 'ping') {
-        response = { success: true };
-    }
-
-    if (!response) {
-        response = {
-            success: false,
-            error: { message: `unknown response for method ${method}` },
-        };
-    }
+    const response = (() => {
+        switch (method) {
+            case 'init':
+            case 'ping':
+                return { success: true };
+            default:
+                return {
+                    success: false,
+                    error: {
+                        code: 'METHOD_NOT_FOUND',
+                        message: `Unknown method: ${method}`,
+                        details: request
+                    }
+                };
+        }
+    })();

     client.send(JSON.stringify({ ...response, id }));
 }

117-128: Add test for concurrent reconnection attempts.

While the current test covers basic reconnection, it would be valuable to test concurrent reconnection attempts.

it('handles concurrent reconnection attempts', async () => {
    const cli = new Client({ url: server.getUrl() });
    await cli.connect();
    
    // Trigger multiple reconnections concurrently
    const reconnectPromises = Array(3).fill(0).map(async () => {
        cli.disconnect(); // sync
        await cli.connect();
    });
    
    await Promise.all(reconnectPromises);
    
    const resp = await cli.sendMessage({ method: 'init' });
    expect(resp.success).toEqual(true);
    
    await cli.disconnect();
});
packages/websocket-client/src/client.ts (4)

126-129: Consider using undefined instead of void in the union type.

The return type of messageValidation uses void in a union type which can be confusing. Consider using undefined for better type clarity.

    protected onMessage(
        message: WebsocketResponse,
-       messageValidation?: (data: Record<string, any>) => Record<string, any> | void,
+       messageValidation?: (data: Record<string, any>) => Record<string, any> | undefined,
    ) {
🧰 Tools
🪛 Biome (1.9.4)

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

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


54-64: Enhance URL validation for better security.

The current URL validation could be more robust. Consider:

  1. Validating URL format using URL constructor
  2. Checking for valid WebSocket protocols (ws/wss)
    protected initWebsocket({ url, timeout, headers, agent }: WebsocketOptions) {
        // url validation
        if (typeof url !== 'string') {
            throw new Error('websocket_no_url');
        }
+       try {
+           const parsedUrl = new URL(url);
+           if (!['ws:', 'wss:', 'http:', 'https:'].includes(parsedUrl.protocol)) {
+               throw new Error('websocket_invalid_protocol');
+           }
+       } catch {
+           throw new Error('websocket_invalid_url');
+       }
+
        if (url.startsWith('http')) {
            url = url.replace('http', 'ws');
        }

        return new WebSocket(url, { timeout, headers, agent });
    }

141-143: Add error logging in catch block.

Empty catch blocks can hide important errors. Consider logging the error or emitting an event.

-       } catch {
-           // empty
+       } catch (error) {
+           this.emitter.emit('error', 'Failed to parse message');
        }

122-126: Consider implementing generic type for message data.

The TODO comment suggests adding generic type support for message data, which would improve type safety.

Would you like me to help implement the generic type support for message data? This would make the WebSocket client more type-safe when handling different message formats.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 715ea47 and 039a639.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (24)
  • CODEOWNERS (1 hunks)
  • packages/blockchain-link/jest.config.unit.js (0 hunks)
  • packages/blockchain-link/package.json (2 hunks)
  • packages/blockchain-link/src/workers/baseWebsocket.ts (1 hunks)
  • packages/blockchain-link/src/workers/blockbook/websocket.ts (1 hunks)
  • packages/blockchain-link/src/workers/blockfrost/websocket.ts (1 hunks)
  • packages/blockchain-link/tsconfig.json (1 hunks)
  • packages/blockchain-link/tsconfig.lib.json (1 hunks)
  • packages/blockchain-link/webpack/dev.js (0 hunks)
  • packages/blockchain-link/webpack/workers.web.js (0 hunks)
  • packages/connect/e2e/karma.config.js (0 hunks)
  • packages/trezor-user-env-link/package.json (1 hunks)
  • packages/trezor-user-env-link/src/websocket-client.ts (4 hunks)
  • packages/trezor-user-env-link/tsconfig.json (1 hunks)
  • packages/websocket-client/README.md (1 hunks)
  • packages/websocket-client/package.json (1 hunks)
  • packages/websocket-client/src/client.ts (1 hunks)
  • packages/websocket-client/src/index.ts (1 hunks)
  • packages/websocket-client/src/ws-browser.ts (2 hunks)
  • packages/websocket-client/src/ws-native.ts (2 hunks)
  • packages/websocket-client/tests/client.test.ts (1 hunks)
  • packages/websocket-client/tsconfig.json (1 hunks)
  • packages/websocket-client/tsconfig.lib.json (1 hunks)
  • suite-native/app/e2e/utils.ts (1 hunks)
💤 Files with no reviewable changes (4)
  • packages/connect/e2e/karma.config.js
  • packages/blockchain-link/jest.config.unit.js
  • packages/blockchain-link/webpack/dev.js
  • packages/blockchain-link/webpack/workers.web.js
🚧 Files skipped from review as they are similar to previous changes (15)
  • packages/websocket-client/tsconfig.json
  • packages/blockchain-link/tsconfig.json
  • packages/websocket-client/src/index.ts
  • packages/blockchain-link/tsconfig.lib.json
  • packages/trezor-user-env-link/tsconfig.json
  • packages/websocket-client/tsconfig.lib.json
  • suite-native/app/e2e/utils.ts
  • CODEOWNERS
  • packages/blockchain-link/src/workers/blockbook/websocket.ts
  • packages/websocket-client/package.json
  • packages/trezor-user-env-link/package.json
  • packages/blockchain-link/src/workers/blockfrost/websocket.ts
  • packages/websocket-client/README.md
  • packages/websocket-client/src/ws-browser.ts
  • packages/blockchain-link/package.json
🧰 Additional context used
🪛 Biome (1.9.4)
packages/websocket-client/src/client.ts

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

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

⏰ Context from checks skipped due to timeout of 90000ms (24)
  • GitHub Check: Unit Tests
  • GitHub Check: Releases revision Checks
  • GitHub Check: Linting and formatting
  • GitHub Check: Other Checks
  • GitHub Check: Type Checking
  • GitHub Check: Build libs for publishing
  • GitHub Check: PR-check / node-override init-api-flaky
  • GitHub Check: PR-check / node-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: PR-check / web-override init-api-flaky
  • GitHub Check: PR-check / web-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: build-deploy
  • GitHub Check: run-desktop-tests (@group=wallet, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: run-desktop-tests (@group=other, trezor-user-env-unix)
  • GitHub Check: EAS Update
  • GitHub Check: run-desktop-tests (@group=settings, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: transport-e2e-test
  • GitHub Check: run-desktop-tests (@group=device-management, trezor-user-env-unix)
  • GitHub Check: prepare_android_test_app
  • GitHub Check: run-desktop-tests (@group=suite, trezor-user-env-unix)
  • GitHub Check: Analyze with CodeQL (javascript)
  • GitHub Check: build-web
  • GitHub Check: build-web
  • GitHub Check: test
  • GitHub Check: Socket Security: Pull Request Alerts
🔇 Additional comments (8)
packages/websocket-client/src/ws-native.ts (2)

49-51: LGTM! Good use of static constant.

Using WSWrapper.OPEN instead of a magic number improves code readability and maintainability.


55-57: LGTM! Good defensive programming.

Adding a state check before sending messages prevents potential race conditions and provides clear error messages.

packages/blockchain-link/src/workers/baseWebsocket.ts (1)

22-36: LGTM! Good error handling and subscription management.

The implementation properly handles:

  • Error cases with CustomError
  • Message resolution
  • Subscription callbacks
packages/trezor-user-env-link/src/websocket-client.ts (2)

41-47: LGTM! Clean implementation of required methods.

The implementation is straightforward and follows the base class contract.


82-89: Address the TODO comment about breaking change.

The comment indicates a potential breaking change in the disconnect behavior. This should be documented and communicated to users.

Would you like me to help create a migration guide for this breaking change?

packages/websocket-client/tests/client.test.ts (1)

97-115: LGTM! Good use of Jest fake timers.

The test properly sets up and cleans up fake timers, and verifies the ping behavior.

packages/websocket-client/src/client.ts (2)

5-18: LGTM! Well-structured type definitions with comprehensive options.

The type definitions provide good type safety and extensibility. The separation between basic WebsocketOptions and extended Options is clean and logical.


30-41: LGTM! Well-structured class with proper encapsulation.

The class extends TypedEmitter appropriately and maintains good encapsulation of member variables with proper access modifiers.

packages/blockchain-link/src/workers/baseWebsocket.ts Outdated Show resolved Hide resolved
packages/websocket-client/src/client.ts Outdated Show resolved Hide resolved
packages/websocket-client/src/client.ts Outdated Show resolved Hide resolved
Copy link

@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

♻️ Duplicate comments (1)
packages/websocket-client/src/client.ts (1)

111-120: ⚠️ Potential issue

Fix binary message handling in sendRawMessage.

The binary flag is incorrectly set. It should be true for binary data (Buffer/ArrayBuffer), not for non-string data.

 ws.send(message, {
-    binary: typeof message !== 'string',
+    binary: message instanceof Buffer || message instanceof ArrayBuffer,
 });
🧹 Nitpick comments (8)
packages/websocket-client/src/client.ts (3)

5-28: Add JSDoc comments to document type definitions.

The type definitions are well-structured, but adding JSDoc comments would improve code maintainability by documenting the purpose and constraints of each option.

+/**
+ * Base WebSocket connection options
+ */
 type WebsocketOptions = {
+    /** WebSocket server URL */
     url: string;
+    /** Request timeout in milliseconds */
     timeout?: number;
+    /** HTTP/HTTPS agent for proxy support */
     agent?: WebSocket.ClientOptions['agent'];
+    /** Additional HTTP headers */
     headers?: WebSocket.ClientOptions['headers'];
 };

54-64: Enhance URL validation.

The current URL validation could be improved to handle more edge cases:

  • Support for wss protocol
  • Validation of malformed URLs
  • Validation of URL format using URL constructor
 protected initWebsocket({ url, timeout, headers, agent }: WebsocketOptions) {
     if (typeof url !== 'string') {
         throw new Error('websocket_no_url');
     }
+    try {
+        const wsUrl = new URL(url);
+        if (wsUrl.protocol === 'http:') {
+            wsUrl.protocol = 'ws:';
+        } else if (wsUrl.protocol === 'https:') {
+            wsUrl.protocol = 'wss:';
+        } else if (!['ws:', 'wss:'].includes(wsUrl.protocol)) {
+            throw new Error('Invalid protocol');
+        }
+        url = wsUrl.toString();
+    } catch (error) {
+        throw new Error('Invalid WebSocket URL');
+    }
     return new WebSocket(url, { timeout, headers, agent });
 }

148-197: Improve connection cleanup on timeout.

The connection timeout handler could leak resources if the WebSocket connection is in an intermediate state.

 const connectionTimeout = setTimeout(
     () => {
         ws.emit('error', new Error('websocket_timeout'));
         try {
             ws.once('error', () => {});
             ws.close();
+            ws.removeAllListeners();
         } catch {
             // empty
         }
     },
     this.options.connectionTimeout || this.options.timeout || DEFAULT_TIMEOUT,
 );
packages/blockchain-link/src/workers/baseWebsocket.ts (2)

24-38: Enhance error handling in onMessage.

The error handling could be improved by:

  1. Adding specific error types
  2. Logging errors for debugging
  3. Handling message parsing errors
 protected onMessage(message: WebsocketResponse) {
     super.onMessage(message, ({ id, data }) => {
         if (data.error) {
-            throw new CustomError('websocket_error_message', data.error.message);
+            const errorMessage = data.error.message || 'Unknown error';
+            const errorCode = data.error.code || 'websocket_error_message';
+            throw new CustomError(errorCode, errorMessage);
         }

         const messageSettled = this.messages.resolve(Number(id), data);
         if (!messageSettled) {
             const subs = this.subscriptions.find(s => s.id === id);
             if (subs) {
                 subs.callback(data);
             }
         }
     });
 }

40-43: Improve error handling in sendMessage.

The error handling in sendMessage could be improved by preserving the original error stack trace.

 protected sendMessage(message: WebsocketRequest) {
     return super.sendMessage(message).catch(error => {
-        throw new CustomError(error.message);
+        throw new CustomError('websocket_send_error', error.message, { cause: error });
     });
 }
packages/trezor-user-env-link/src/websocket-client.ts (3)

Line range hint 13-23: Document timeout configuration rationale.

The high timeout values and IP address configuration should be documented to explain:

  • Why 5-minute timeout is necessary for emulator actions
  • Why localhost resolution needs the IP6 workaround
-// Making the timeout high because the controller in trezor-user-env
-// must synchronously run actions on emulator and they may take a long time
-// (for example in case of Shamir backup)
+/**
+ * Extended timeout (5 minutes) is required because:
+ * 1. The controller in trezor-user-env runs actions synchronously
+ * 2. Some operations like Shamir backup can take several minutes
+ * 3. Emulator operations may be slower than real device operations
+ */
 const DEFAULT_TIMEOUT = 5 * 60 * 1000;
 const DEFAULT_PING_TIMEOUT = 50 * 1000;

-// breaking change in node 17, ip6 is preferred by default
-// localhost is not resolved correctly on certain machines
+/**
+ * Using explicit IPv4 address because:
+ * 1. Node 17+ prefers IPv6 by default
+ * 2. localhost resolution fails on some machines
+ * 3. Ensures consistent connection behavior across environments
+ */
 const USER_ENV_URL = {

94-105: Improve error handling in onMessage.

The error handling could be enhanced by:

  1. Adding specific error types for different failure scenarios
  2. Including more context in error messages
 protected onMessage(message: WebsocketResponseData) {
     super.onMessage(message, resp => {
         if (resp.type === 'client') {
             this.emit('firmwares', resp.firmwares);
         } else {
             if (!resp.success) {
-                throw new Error(`websocket_error_message: ${resp.error.message || resp.error}`);
+                const context = resp.type ? ` (type: ${resp.type})` : '';
+                const errorMessage = resp.error.message || resp.error;
+                throw new Error(`websocket_error_message: ${errorMessage}${context}`);
             }
         }
         return resp;
     });
 }

Line range hint 107-143: Improve waitForTrezorUserEnv implementation.

The current implementation has several areas for improvement:

  1. Magic numbers should be constants
  2. Error handling could be more informative
  3. Progress logging could be more consistent
+const WAIT_TIMEOUT_SECONDS = 300;
+const WAIT_INTERVAL_MS = 1000;
+
 async waitForTrezorUserEnv() {
-    const limit = 300;
     let error = '';
 
     console.log('waiting for trezor-user-env');
 
-    for (let i = 0; i < limit; i++) {
-        if (i === limit - 1) {
+    for (let i = 0; i < WAIT_TIMEOUT_SECONDS; i++) {
+        if (i === WAIT_TIMEOUT_SECONDS - 1) {
             console.log(`cant connect to trezor-user-env: ${error}\n`);
         }
-        await delay(1000);
+        await delay(WAIT_INTERVAL_MS);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 039a639 and 8e01f39.

📒 Files selected for processing (3)
  • packages/blockchain-link/src/workers/baseWebsocket.ts (1 hunks)
  • packages/trezor-user-env-link/src/websocket-client.ts (4 hunks)
  • packages/websocket-client/src/client.ts (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/websocket-client/src/client.ts

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

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

⏰ Context from checks skipped due to timeout of 90000ms (23)
  • GitHub Check: Unit Tests
  • GitHub Check: Releases revision Checks
  • GitHub Check: Build libs for publishing
  • GitHub Check: Other Checks
  • GitHub Check: Linting and formatting
  • GitHub Check: Type Checking
  • GitHub Check: PR-check / node-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: PR-check / node-override init-api-flaky
  • GitHub Check: PR-check / web-override init-api-flaky
  • GitHub Check: PR-check / web-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: build-deploy
  • GitHub Check: prepare_android_test_app
  • GitHub Check: run-desktop-tests (@group=wallet, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: run-desktop-tests (@group=other, trezor-user-env-unix)
  • GitHub Check: EAS Update
  • GitHub Check: transport-e2e-test
  • GitHub Check: run-desktop-tests (@group=settings, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: build-web
  • GitHub Check: build-web
  • GitHub Check: run-desktop-tests (@group=device-management, trezor-user-env-unix)
  • GitHub Check: test
  • GitHub Check: Analyze with CodeQL (javascript)
  • GitHub Check: run-desktop-tests (@group=suite, trezor-user-env-unix)
🔇 Additional comments (2)
packages/websocket-client/src/client.ts (1)

45-52: Consider edge cases in timeout handling.

The message manager's timeout could potentially conflict with the connection timeout. Consider adding a mechanism to prevent race conditions between message timeouts and connection timeouts.

packages/trezor-user-env-link/src/websocket-client.ts (1)

82-91: Address TODO comment in disconnect method.

The TODO comment indicates a breaking change in disconnect behavior. This needs to be addressed and documented properly.

✅ Verification successful

TODO comment can be safely removed

The current implementation correctly handles the breaking change by:

  • Only disconnecting when actually connected
  • Properly cleaning up listeners before disconnection
  • Maintaining consistent Promise-based API
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for disconnect usage patterns
rg "\.disconnect\(\)" --type ts -A 2

Length of output: 14809


Script:

#!/bin/bash
# Find the base class implementation
ast-grep --pattern 'class $_ {
  $$$
  disconnect() {
    $$$
  }
  $$$
}'

Length of output: 26680

@szymonlesisz
Copy link
Contributor Author

/rebase

Copy link

@trezor-ci trezor-ci force-pushed the feat/websocket-package branch from 8e01f39 to 4678201 Compare January 30, 2025 19:55
Copy link

@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 (10)
packages/trezor-user-env-link/src/websocket-client.ts (3)

49-49: Consider using a more specific type for options.

Using any type reduces type safety. Consider creating a dedicated interface for the options.

-    constructor(options: any = {}) {
+    interface WebsocketClientOptions {
+        url?: string;
+        timeout?: number;
+        pingTimeout?: number;
+        [key: string]: unknown;
+    }
+    constructor(options: WebsocketClientOptions = {}) {

94-105: Consider enhancing error handling.

The current error handling could be improved by:

  1. Using custom error classes
  2. Extracting the success check logic
+class WebsocketResponseError extends Error {
+    constructor(message: string) {
+        super(message);
+        this.name = 'WebsocketResponseError';
+    }
+}

+private isSuccessResponse(resp: WebsocketResponse): boolean {
+    return resp.success === true;
+}

 protected onMessage(message: WebsocketResponseData) {
     super.onMessage(message, resp => {
         if (resp.type === 'client') {
             this.emit('firmwares', resp.firmwares);
         } else {
-            if (!resp.success) {
-                throw new Error(`websocket_error_message: ${resp.error.message || resp.error}`);
+            if (!this.isSuccessResponse(resp)) {
+                throw new WebsocketResponseError(`websocket_error_message: ${resp.error.message || resp.error}`);
             }
         }
         return resp;
     });
 }

Line range hint 108-143: Enhance waitForTrezorUserEnv implementation.

Consider the following improvements:

  1. Make retry limit configurable
  2. Replace console.log with proper logging
  3. Add exponential backoff for retries

Would you like me to propose a refactored implementation with these improvements?

packages/websocket-client/src/client.ts (4)

19-20: Document timeout constants.

Consider adding JSDoc comments to explain the purpose and implications of these timeout values.

+/** Default timeout for individual WebSocket operations in milliseconds */
 const DEFAULT_TIMEOUT = 20 * 1000;
+/** Default ping interval in milliseconds to keep the connection alive */
 const DEFAULT_PING_TIMEOUT = 50 * 1000;

54-64: Enhance URL validation.

The current URL validation could be more robust by:

  1. Validating the protocol (ws/wss)
  2. Ensuring the URL is well-formed
 protected initWebsocket({ url, timeout, headers, agent }: WebsocketOptions) {
     // url validation
     if (typeof url !== 'string') {
         throw new Error('websocket_no_url');
     }
+    // Validate protocol
+    if (!url.match(/^(ws|wss|http|https):\/\/.+/)) {
+        throw new Error('websocket_invalid_protocol');
+    }
     if (url.startsWith('http')) {
         url = url.replace('http', 'ws');
     }

115-117: Improve binary message type check.

The current binary flag check could be more explicit about the types of binary data.

 ws.send(message, {
-    binary: typeof message !== 'string',
+    binary: message instanceof Buffer || message instanceof ArrayBuffer || message instanceof Uint8Array,
 });

169-169: Enhance connection timeout error message.

Consider providing more context in the timeout error message.

-ws.emit('error', new Error('websocket_timeout'));
+ws.emit('error', new Error(`websocket_connection_timeout: Failed to establish connection to ${this.options.url} within ${this.options.connectionTimeout || this.options.timeout || DEFAULT_TIMEOUT}ms`));
packages/websocket-client/tests/client.test.ts (2)

34-55: Add error handling to sendResponse.

The server's message handling should include try-catch blocks for JSON parsing and message processing.

 private sendResponse(client: WebSocket, data: any) {
+    try {
         const request = JSON.parse(data);
         const { id, method } = request;
         let response;

         if (method === 'init') {
             response = { success: true };
         }

         if (method === 'ping') {
             response = { success: true };
         }

         if (!response) {
             response = {
                 success: false,
                 error: { message: `unknown response for method ${method}` },
             };
         }

         client.send(JSON.stringify({ ...response, id }));
+    } catch (error) {
+        client.send(JSON.stringify({
+            success: false,
+            error: { message: 'Invalid message format' },
+        }));
+    }
     }

69-164: Add test cases for edge scenarios.

Consider adding tests for:

  1. Handling malformed messages
  2. Connection with invalid headers
  3. Reconnection with backoff
  4. Maximum reconnection attempts

Would you like me to generate the additional test cases?

packages/blockchain-link/src/workers/baseWebsocket.ts (1)

24-38: Consider adding message validation types.

The onMessage implementation could benefit from stronger typing for the message validation callback.

+type MessageValidator<T> = (data: { id: string; data: any }) => T;

 protected onMessage(message: WebsocketResponse) {
-    super.onMessage(message, ({ id, data }) => {
+    super.onMessage(message, (({ id, data }): MessageValidator<T[keyof T]> => {
         if (data.error) {
             throw new CustomError('websocket_error_message', data.error.message);
         }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8e01f39 and 4678201.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (24)
  • CODEOWNERS (1 hunks)
  • packages/blockchain-link/jest.config.unit.js (0 hunks)
  • packages/blockchain-link/package.json (2 hunks)
  • packages/blockchain-link/src/workers/baseWebsocket.ts (1 hunks)
  • packages/blockchain-link/src/workers/blockbook/websocket.ts (1 hunks)
  • packages/blockchain-link/src/workers/blockfrost/websocket.ts (1 hunks)
  • packages/blockchain-link/tsconfig.json (1 hunks)
  • packages/blockchain-link/tsconfig.lib.json (1 hunks)
  • packages/blockchain-link/webpack/dev.js (0 hunks)
  • packages/blockchain-link/webpack/workers.web.js (0 hunks)
  • packages/connect/e2e/karma.config.js (0 hunks)
  • packages/trezor-user-env-link/package.json (1 hunks)
  • packages/trezor-user-env-link/src/websocket-client.ts (4 hunks)
  • packages/trezor-user-env-link/tsconfig.json (1 hunks)
  • packages/websocket-client/README.md (1 hunks)
  • packages/websocket-client/package.json (1 hunks)
  • packages/websocket-client/src/client.ts (1 hunks)
  • packages/websocket-client/src/index.ts (1 hunks)
  • packages/websocket-client/src/ws-browser.ts (2 hunks)
  • packages/websocket-client/src/ws-native.ts (2 hunks)
  • packages/websocket-client/tests/client.test.ts (1 hunks)
  • packages/websocket-client/tsconfig.json (1 hunks)
  • packages/websocket-client/tsconfig.lib.json (1 hunks)
  • suite-native/app/e2e/utils.ts (1 hunks)
💤 Files with no reviewable changes (4)
  • packages/blockchain-link/jest.config.unit.js
  • packages/blockchain-link/webpack/dev.js
  • packages/blockchain-link/webpack/workers.web.js
  • packages/connect/e2e/karma.config.js
🚧 Files skipped from review as they are similar to previous changes (16)
  • packages/blockchain-link/tsconfig.lib.json
  • packages/blockchain-link/tsconfig.json
  • CODEOWNERS
  • packages/websocket-client/src/index.ts
  • suite-native/app/e2e/utils.ts
  • packages/trezor-user-env-link/tsconfig.json
  • packages/websocket-client/tsconfig.json
  • packages/websocket-client/tsconfig.lib.json
  • packages/blockchain-link/src/workers/blockfrost/websocket.ts
  • packages/websocket-client/src/ws-native.ts
  • packages/websocket-client/README.md
  • packages/websocket-client/src/ws-browser.ts
  • packages/blockchain-link/src/workers/blockbook/websocket.ts
  • packages/websocket-client/package.json
  • packages/trezor-user-env-link/package.json
  • packages/blockchain-link/package.json
🧰 Additional context used
📓 Learnings (1)
packages/blockchain-link/src/workers/baseWebsocket.ts (1)
Learnt from: szymonlesisz
PR: trezor/trezor-suite#15797
File: packages/blockchain-link/src/workers/baseWebsocket.ts:10-12
Timestamp: 2025-01-30T19:51:50.844Z
Learning: BaseWebsocket instances in @trezor/blockchain-link are designed to be disposed after onClose, making explicit cleanup of subscriptions unnecessary as they will be handled by garbage collection.
🪛 Biome (1.9.4)
packages/websocket-client/src/client.ts

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

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

⏰ Context from checks skipped due to timeout of 90000ms (24)
  • GitHub Check: Type Checking
  • GitHub Check: Releases revision Checks
  • GitHub Check: Other Checks
  • GitHub Check: Build libs for publishing
  • GitHub Check: Linting and formatting
  • GitHub Check: Unit Tests
  • GitHub Check: PR-check / node-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: PR-check / node-override init-api-flaky
  • GitHub Check: PR-check / web-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: PR-check / web-override init-api-flaky
  • GitHub Check: build-deploy
  • GitHub Check: build-deploy
  • GitHub Check: prepare_android_test_app
  • GitHub Check: run-desktop-tests (@group=wallet, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: run-desktop-tests (@group=other, trezor-user-env-unix)
  • GitHub Check: run-desktop-tests (@group=settings, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: EAS Update
  • GitHub Check: run-desktop-tests (@group=device-management, trezor-user-env-unix)
  • GitHub Check: transport-e2e-test
  • GitHub Check: run-desktop-tests (@group=suite, trezor-user-env-unix)
  • GitHub Check: build-web
  • GitHub Check: test
  • GitHub Check: build-web
  • GitHub Check: Analyze with CodeQL (javascript)
🔇 Additional comments (8)
packages/trezor-user-env-link/src/websocket-client.ts (4)

5-8: LGTM! Imports align with the PR objectives.

The imports from @trezor/websocket-client correctly implement the shared package strategy.


40-46: LGTM! Clean implementation of base class methods.

The createWebsocket and ping methods provide clear, focused implementations of the base class requirements.


Line range hint 66-80: Consider removing the karma workaround.

The comment indicates this is temporary due to planned karma removal. Consider creating a tracking issue for proper CORS headers in the trezor-user-env server.

Would you like me to create an issue to track:

  1. Adding proper CORS headers in trezor-user-env server
  2. Removing the karma workaround after the migration?

82-91: Address the breaking change in disconnect method.

The TODO comment indicates a significant behavioral change where disconnect no longer acts like dispose. This breaking change should be:

  1. Documented in the migration guide
  2. Communicated to dependent packages
  3. Potentially versioned as a major release

Let's verify the impact:

✅ Verification successful

Breaking change in disconnect method appears to be properly managed

The codebase analysis shows that the behavioral change in disconnect() is well-tested and properly handled across the codebase. Most usages are in test scenarios or cleanup contexts where the behavior is explicitly verified.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for direct calls to disconnect() to identify affected code
rg -A 2 "\.disconnect\(\)" --type ts

Length of output: 14809

packages/websocket-client/src/client.ts (2)

30-52: LGTM! Well-structured class initialization.

The class is well-designed with proper type safety and initialization.


128-128: LGTM! Correct usage of void type.

The void type in the union is intentional here, representing callbacks that don't return a value. Using undefined instead (as suggested by static analysis) would be semantically incorrect.

🧰 Tools
🪛 Biome (1.9.4)

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

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

packages/blockchain-link/src/workers/baseWebsocket.ts (2)

13-22: LGTM! Proper ping handling with error management.

The onPing implementation correctly handles both subscription-based and keepAlive scenarios with proper error handling.


40-43: LGTM! Proper error handling in sendMessage.

The implementation correctly wraps errors in CustomError and aligns with the learning about instance lifecycle and garbage collection.

@mroz22
Copy link
Contributor

mroz22 commented Jan 31, 2025

one last fixup fcd363d

Copy link
Member

@karliatto karliatto left a comment

Choose a reason for hiding this comment

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

Non tested ACK

@szymonlesisz
Copy link
Contributor Author

one last fixup

and another one from me :) 9a62fc2

Non tested ACK

tested (by CI) more than i wished :)

@mroz22
Copy link
Contributor

mroz22 commented Jan 31, 2025

/rebase

Copy link

@trezor-ci trezor-ci force-pushed the feat/websocket-package branch from 9a62fc2 to c4af911 Compare January 31, 2025 20:03
Copy link

@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/websocket-client/src/client.ts (1)

126-129: Replace void with undefined in union type.

The use of void in the union type can be confusing. Using undefined is more appropriate in this context.

    protected onMessage(
        message: WebsocketResponse,
-       messageValidation?: (data: Record<string, any>) => Record<string, any> | void,
+       messageValidation?: (data: Record<string, any>) => Record<string, any> | undefined,
    ) {
🧰 Tools
🪛 Biome (1.9.4)

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

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

packages/trezor-user-env-link/src/websocket-client.ts (2)

49-49: Replace any type with a proper interface.

Using any type reduces type safety. Consider creating a proper interface for the options parameter.

-constructor(options: any = {}) {
+interface WebsocketClientOptions {
+    url?: string;
+    timeout?: number;
+    pingTimeout?: number;
+    [key: string]: any; // for other options
+}
+constructor(options: WebsocketClientOptions = {}) {

94-105: Enhance error handling with specific error types.

The error handling could be improved by using specific error types instead of generic Error class.

+class WebsocketResponseError extends Error {
+    constructor(message: string) {
+        super(message);
+        this.name = 'WebsocketResponseError';
+    }
+}

-throw new Error(`websocket_error_message: ${resp.error.message || resp.error}`);
+throw new WebsocketResponseError(`websocket_error_message: ${resp.error.message || resp.error}`);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9a62fc2 and c4af911.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (24)
  • CODEOWNERS (1 hunks)
  • packages/blockchain-link/jest.config.unit.js (0 hunks)
  • packages/blockchain-link/package.json (2 hunks)
  • packages/blockchain-link/src/workers/baseWebsocket.ts (1 hunks)
  • packages/blockchain-link/src/workers/blockbook/websocket.ts (1 hunks)
  • packages/blockchain-link/src/workers/blockfrost/websocket.ts (1 hunks)
  • packages/blockchain-link/tsconfig.json (1 hunks)
  • packages/blockchain-link/tsconfig.lib.json (1 hunks)
  • packages/blockchain-link/webpack/dev.js (0 hunks)
  • packages/blockchain-link/webpack/workers.web.js (0 hunks)
  • packages/connect/e2e/karma.config.js (0 hunks)
  • packages/trezor-user-env-link/package.json (1 hunks)
  • packages/trezor-user-env-link/src/websocket-client.ts (4 hunks)
  • packages/trezor-user-env-link/tsconfig.json (1 hunks)
  • packages/websocket-client/README.md (1 hunks)
  • packages/websocket-client/package.json (1 hunks)
  • packages/websocket-client/src/client.ts (1 hunks)
  • packages/websocket-client/src/index.ts (1 hunks)
  • packages/websocket-client/src/ws-browser.ts (2 hunks)
  • packages/websocket-client/src/ws-native.ts (2 hunks)
  • packages/websocket-client/tests/client.test.ts (1 hunks)
  • packages/websocket-client/tsconfig.json (1 hunks)
  • packages/websocket-client/tsconfig.lib.json (1 hunks)
  • suite-native/app/e2e/utils.ts (1 hunks)
💤 Files with no reviewable changes (4)
  • packages/blockchain-link/jest.config.unit.js
  • packages/blockchain-link/webpack/dev.js
  • packages/connect/e2e/karma.config.js
  • packages/blockchain-link/webpack/workers.web.js
🚧 Files skipped from review as they are similar to previous changes (17)
  • packages/blockchain-link/tsconfig.lib.json
  • CODEOWNERS
  • packages/blockchain-link/tsconfig.json
  • packages/websocket-client/src/index.ts
  • packages/websocket-client/tsconfig.lib.json
  • packages/websocket-client/tsconfig.json
  • packages/trezor-user-env-link/tsconfig.json
  • packages/websocket-client/README.md
  • packages/blockchain-link/src/workers/blockbook/websocket.ts
  • suite-native/app/e2e/utils.ts
  • packages/blockchain-link/src/workers/blockfrost/websocket.ts
  • packages/websocket-client/src/ws-browser.ts
  • packages/trezor-user-env-link/package.json
  • packages/websocket-client/src/ws-native.ts
  • packages/websocket-client/package.json
  • packages/blockchain-link/package.json
  • packages/websocket-client/tests/client.test.ts
🧰 Additional context used
📓 Learnings (1)
packages/blockchain-link/src/workers/baseWebsocket.ts (1)
Learnt from: szymonlesisz
PR: trezor/trezor-suite#15797
File: packages/blockchain-link/src/workers/baseWebsocket.ts:10-12
Timestamp: 2025-01-30T19:51:50.844Z
Learning: BaseWebsocket instances in @trezor/blockchain-link are designed to be disposed after onClose, making explicit cleanup of subscriptions unnecessary as they will be handled by garbage collection.
🪛 Biome (1.9.4)
packages/websocket-client/src/client.ts

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

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

⏰ Context from checks skipped due to timeout of 90000ms (16)
  • GitHub Check: Setup and Cache Dependencies
  • GitHub Check: build-deploy
  • GitHub Check: build-deploy
  • GitHub Check: run-desktop-tests (@group=wallet, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: run-desktop-tests (@group=other, trezor-user-env-unix)
  • GitHub Check: EAS Update
  • GitHub Check: run-desktop-tests (@group=settings, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: transport-e2e-test
  • GitHub Check: test
  • GitHub Check: run-desktop-tests (@group=device-management, trezor-user-env-unix)
  • GitHub Check: build-web
  • GitHub Check: prepare_android_test_app
  • GitHub Check: run-desktop-tests (@group=suite, trezor-user-env-unix)
  • GitHub Check: Analyze with CodeQL (javascript)
  • GitHub Check: build-web
  • GitHub Check: Socket Security: Pull Request Alerts
🔇 Additional comments (10)
packages/blockchain-link/src/workers/baseWebsocket.ts (3)

13-22: LGTM! Robust ping handling implementation.

The implementation correctly handles ping failures and disconnection scenarios based on subscription state and keepAlive flag.


24-38: LGTM! Well-structured message handling.

The implementation correctly:

  • Validates and routes messages
  • Handles errors appropriately
  • Manages subscriptions

40-43: LGTM! Clean error handling.

The implementation properly wraps WebSocket errors in domain-specific CustomError.

packages/websocket-client/src/client.ts (5)

5-28: LGTM! Well-structured type definitions.

The types and constants are well-defined with reasonable defaults.


95-109: LGTM! Robust message sending implementation.

The implementation properly handles message IDs, timeouts, and callbacks.


111-120: LGTM! Correct binary message handling.

The implementation properly sets the binary flag based on message type.


148-230: LGTM! Comprehensive connection management.

The implementation:

  • Handles connection timeouts
  • Manages connection states properly
  • Includes proper error handling
  • Handles edge cases like already connecting state

232-244: LGTM! Thorough cleanup implementation.

The implementation properly cleans up resources and handles pending operations.

packages/trezor-user-env-link/src/websocket-client.ts (2)

40-55: LGTM! Well-structured class inheritance.

The class extension is well-implemented with proper type parameters and default configurations. The protected methods provide good extension points for customization.


Line range hint 66-80: Consider removing karma workaround.

The workaround for karma is noted as temporary since karma will be removed. Track this technical debt to ensure it's addressed when karma is removed.

Would you like me to create an issue to track the removal of this workaround when karma is deprecated?

@mroz22 mroz22 merged commit a8cd2f4 into develop Feb 1, 2025
74 checks passed
@mroz22 mroz22 deleted the feat/websocket-package branch February 1, 2025 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no-project This label is used to specify that PR doesn't need to be added to a project
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants