Skip to content

Conversation

@ofreyssinet-ledger
Copy link
Contributor

@ofreyssinet-ledger ofreyssinet-ledger commented Dec 1, 2025

📝 Description

This PR introduces a new way to implement developer tools for the DMK.
There was already an existing implementation relying on Flipper, but as it is deprecated, we need a new solution.

For this new implementation, I chose to not depend on any third party, but keep the door open in case some frameworks can help us. It's the case of Rozenite which is a developer tools platform for React Native apps.

The core of the work done here is the building of a bi-directional communication bridge between a client app (hosting the DMK) and an app called "Dashboard". It's a platform on top of which we can build actual developer tools that will have their UI in the "Dashboard" app.

DMK DevTools archi (3)

There are two "dashboard app" implementations:

  • One using Rozenite, for React Native apps
  • One using Electron, for all other platforms

In terms of features, for now the devtools only support displaying the logs of the DMK.
More features will come, for instance:

  • Interacting with device sessions
    • visualising intent queues
    • live device session state
    • base interaction (apdus/commands)
  • Advanced logging (filters, logs context visualisation)
  • Interacting with the DMK

So the work is structured in several modules:

  • packages/devtools-core:
    • contains the core types used by other modules
    • contains the DevToolsLogger which can be injected in DeviceManagementKitBuilder and will relay all DMK logs to the devtools dashboard.
    • will contain other similar modules in the future
  • packages/devtools-rozenite:
    • this module can be added to a React Native app that has Rozenite enabled and the DMK devtools will automatically appear next to the other dev tools.
  • packages/devtools-ui
    • exports a react component that is responsible for rendering the entire UI of the devtools dashboard
  • packages/devtools-websocket-common
    • common types and constants used for the websocket layer of the devtools
  • packages/devtools-websocket-connector
    • implementation of a Connector that is able to talk with the websocket server
  • packages/devtools-websocket-server
    • implementation of a node app hosting 2 WebSocket servers (one for the client and one for the dashboard), and relaying messages between them
  • apps/devtools
    • Electron app hosting the devtools-websocket-server as well as devtools-ui so that a client app using the devtools-websocket-connector can connect to it and have some working developer tools.
    • For now it's not ready to be released, it's still a WIP but I included it because it's fully functional in dev mode.

Demo

Rozenite

rozenite.devtools.mp4

WebSocket + Electron

Screen.Recording.2025-12-01.at.20.49.56.mp4

ℹ️ NB: >8k lines modified, but >5k are due to the addition of several packages and changes in the pnpm lockfile related to the addition of an Electron project

❓ Context

  • JIRA or GitHub link: [NO-ISSUE]
  • Feature:

✅ Checklist

Pull Requests must pass CI checks and undergo code review. Set the PR as Draft if it is not yet ready for review.

  • Covered by automatic tests
  • Changeset is provided
  • Documentation is up-to-date
  • Impact of the changes:
    • list of the changes

🧐 Checklist for the PR Reviewers

  • The code aligns with the requirements described in the linked JIRA or GitHub issue.
  • The PR description clearly documents the changes made and explains any technical trade-offs or design decisions.
  • There are no undocumented trade-offs, technical debt, or maintainability issues.
  • The PR has been tested thoroughly, and any potential edge cases have been considered and handled.
  • Any new dependencies have been justified and documented.

@vercel
Copy link

vercel bot commented Dec 1, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
device-sdk-ts-sample Ready Ready Preview, Comment Jan 9, 2026 3:02pm
1 Skipped Deployment
Project Deployment Review Updated (UTC)
doc-device-management-kit Ignored Ignored Jan 9, 2026 3:02pm

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a comprehensive developer tools system for the Device Management Kit (DMK) to replace the deprecated Flipper integration. The implementation creates a flexible, bi-directional communication bridge between client applications and a dashboard interface.

Key changes:

  • Removed deprecated Flipper-based devtools packages and integration code
  • Added 7 new devtools packages implementing a modular WebSocket-based architecture
  • Created platform-specific implementations for React Native (Rozenite) and web/Electron
  • Integrated devtools into sample and mobile applications

Reviewed changes

Copilot reviewed 117 out of 132 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
packages/devtools-core/* Core types, interfaces, and DevToolsLogger implementation
packages/devtools-websocket-*/ WebSocket server, connector, and common utilities for bidirectional communication
packages/devtools-ui/* React-based dashboard UI with logger and debugging screens
packages/devtools-rozenite/* Rozenite integration for React Native applications
apps/devtools/* Electron application hosting the dashboard and WebSocket server
apps/sample/* Updated to use new WebSocket-based devtools
apps/mobile/* Integrated Rozenite devtools for React Native
packages/flipper-plugin-client/* Deprecated Flipper code removed
pnpm-workspace.yaml Added Electron and Vite dependencies to catalog

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@ofreyssinet-ledger ofreyssinet-ledger marked this pull request as ready for review December 2, 2025 16:50
@ofreyssinet-ledger ofreyssinet-ledger requested a review from a team as a code owner December 2, 2025 16:50
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 116 out of 131 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

* STATIC METHODS
*/
static instance: RozeniteConnector | null = null;
static getInstance(): RozeniteConnector {

Choose a reason for hiding this comment

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

[ASK] Why do you create a singleton here? You cannot use a DI library?

* STATIC METHODS
*/
static instance: DevtoolsWebSocketConnector | null = null;
static getInstance(): DevtoolsWebSocketConnector {

Choose a reason for hiding this comment

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

[ASK] Same

* STATIC METHODS
*/
static instance: DevtoolsWebSocketConnector | null = null;
static getInstance(): DevtoolsWebSocketConnector {

Choose a reason for hiding this comment

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

[ASK] In. fact I don't not understand how you switch between different impl of Connector because of the usage of the singleton pattern

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@smartine-ledger regarding this comment and the two comments above:

The Connector interface exists so dev tools modules (for instance DevToolsLogger) can accept an instance of a Connector, but the client picks one based on their platform. They're not interchangeable at runtime, they're alternative implementations for different environments:

  • React Native apps: RozeniteConnector
  • Web apps: DevToolsWebSocketConnector

The singleton serves a specific purpose for the developer experience in client apps: The goal is to allow any client app to very easily integrate the DMK devtools without having to setup a dependency injection system.

Why we need it:
The Connector instance needs to be:

  1. Initialized with platform-specific logic (for instance, RozeniteConnector needs to receive a RozeniteDevToolsClient, DevtoolsWebSocketConnector needs a URL to connect to)
  2. Passed to a devtools module (for instance DevToolsLogger) which is then passed to the DMK builder.

These two actions often happen in very different parts of a client application, and not necessarily in that order. The singleton pattern allows both places to access the same instance without requiring a DI setup. The initialisation logic inside the connectors, along with the ReplaySubjects ensures that the two actions can happen in any order.

For the same reason, using a DI lib here does not make sense because the DI would have to be done in the client app, not in the DMK.

Choose a reason for hiding this comment

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

I don't understand why the client needs to setup a DI if you provide a single entry point to your devtool (ex : buildDevToolLogger()) and implement in your side all the logic of the creation of the differents objects through a DI. This will allow to make the package easier to maintain and easily scalable but ok. Let's keep the code unchanged for the moment

url: string;
};

export class DevtoolsWebSocketConnector implements Connector {
Copy link

@smartine-ledger smartine-ledger Dec 15, 2025

Choose a reason for hiding this comment

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

[COMMENT] you can keep it like this for the moment but this class seems to do a lot of thing (initialise the connection / handle the internal state of the connection / format the message through a JSON lib

Comment on lines +37 to +49
client: {
server: null,
connection: null,
messageBuffer: new ReplaySubject(),
messageBufferSubscription: null,
},
dashboard: {
server: null,
connection: null,
messageBuffer: new ReplaySubject(),
messageBufferSubscription: null,
},
};

Choose a reason for hiding this comment

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

[ASK] One class which handles two different kind of connection? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The class has a single responsibility: acting as a message bridge between client and dashboard.
The two connections are fundamentally coupled: messages flow between them, and disconnection of one affects the buffering logic of the other.

Copy link

@smartine-ledger smartine-ledger Jan 9, 2026

Choose a reason for hiding this comment

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

Ok. IMHO it can be simplify but let's keep this way for the moment and we will see in the future if we need to handle additional logic 😉

@github-actions
Copy link
Contributor

github-actions bot commented Jan 8, 2026

Messages
⚠️ No changeset file found. Please make sure this is intended or add a changeset file.

Generated by 🚫 dangerJS against 8c9a63f

@ofreyssinet-ledger ofreyssinet-ledger changed the title ✨ (devtools): DMK devtools for RN & web apps ✨ (devtools) [NO-ISSUE]: DMK devtools for RN & web apps Jan 8, 2026
Comment on lines +37 to +49
client: {
server: null,
connection: null,
messageBuffer: new ReplaySubject(),
messageBufferSubscription: null,
},
dashboard: {
server: null,
connection: null,
messageBuffer: new ReplaySubject(),
messageBufferSubscription: null,
},
};
Copy link

@smartine-ledger smartine-ledger Jan 9, 2026

Choose a reason for hiding this comment

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

Ok. IMHO it can be simplify but let's keep this way for the moment and we will see in the future if we need to handle additional logic 😉

* STATIC METHODS
*/
static instance: DevtoolsWebSocketConnector | null = null;
static getInstance(): DevtoolsWebSocketConnector {

Choose a reason for hiding this comment

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

I don't understand why the client needs to setup a DI if you provide a single entry point to your devtool (ex : buildDevToolLogger()) and implement in your side all the logic of the creation of the differents objects through a DI. This will allow to make the package easier to maintain and easily scalable but ok. Let's keep the code unchanged for the moment

@ofreyssinet-ledger ofreyssinet-ledger added this pull request to the merge queue Jan 9, 2026
Merged via the queue into develop with commit a7c084a Jan 9, 2026
23 checks passed
@ofreyssinet-ledger ofreyssinet-ledger deleted the feat/no-issue-devtools branch January 9, 2026 17:10
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.

4 participants