feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,37 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
file(GLOB hermesinspectormodern_SRC CONFIGURE_DEPENDS chrome/*.cpp)
add_library(hermes_inspector_modern
STATIC
${hermesinspectormodern_SRC})
target_compile_options(
hermes_inspector_modern
PRIVATE
-std=c++20
-fexceptions
)
if(${CMAKE_BUILD_TYPE} MATCHES Debug)
target_compile_options(
hermes_inspector_modern
PRIVATE
-DHERMES_ENABLE_DEBUGGER=1
)
endif()
target_include_directories(hermes_inspector_modern PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(hermes_inspector_modern
jsinspector
fb
glog
hermes-engine::libhermes
jsi
runtimeexecutor)

View File

@@ -0,0 +1,147 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ConnectionDemux.h"
#ifdef HERMES_ENABLE_DEBUGGER
#include <hermes/inspector/RuntimeAdapter.h>
#include <hermes/inspector/chrome/CDPHandler.h>
#include <jsinspector-modern/InspectorInterfaces.h>
namespace facebook {
namespace hermes {
namespace inspector_modern {
namespace chrome {
using ::facebook::react::jsinspector_modern::IInspector;
using ::facebook::react::jsinspector_modern::ILocalConnection;
using ::facebook::react::jsinspector_modern::IRemoteConnection;
namespace {
class LocalConnection : public ILocalConnection {
public:
LocalConnection(
std::shared_ptr<hermes::inspector_modern::chrome::CDPHandler> conn,
std::shared_ptr<std::unordered_set<std::string>> inspectedContexts);
~LocalConnection();
void sendMessage(std::string message) override;
void disconnect() override;
private:
std::shared_ptr<hermes::inspector_modern::chrome::CDPHandler> conn_;
std::shared_ptr<std::unordered_set<std::string>> inspectedContexts_;
};
LocalConnection::LocalConnection(
std::shared_ptr<hermes::inspector_modern::chrome::CDPHandler> conn,
std::shared_ptr<std::unordered_set<std::string>> inspectedContexts)
: conn_(conn), inspectedContexts_(inspectedContexts) {
inspectedContexts_->insert(conn->getTitle());
}
LocalConnection::~LocalConnection() = default;
void LocalConnection::sendMessage(std::string str) {
conn_->handle(std::move(str));
}
void LocalConnection::disconnect() {
inspectedContexts_->erase(conn_->getTitle());
conn_->unregisterCallbacks();
}
} // namespace
ConnectionDemux::ConnectionDemux(
facebook::react::jsinspector_modern::IInspector& inspector)
: globalInspector_(inspector),
inspectedContexts_(std::make_shared<std::unordered_set<std::string>>()) {}
ConnectionDemux::~ConnectionDemux() = default;
DebugSessionToken ConnectionDemux::enableDebugging(
std::unique_ptr<RuntimeAdapter> adapter,
const std::string& title) {
std::scoped_lock lock(mutex_);
// TODO(#22976087): workaround for ComponentScript contexts never being
// destroyed.
//
// After a reload, the old ComponentScript VM instance stays alive. When we
// register the new CS VM instance, check for any previous CS VM (via strcmp
// of title) and remove them.
std::vector<int> pagesToDelete;
for (auto it = conns_.begin(); it != conns_.end(); ++it) {
if (it->second->getTitle() == title) {
pagesToDelete.push_back(it->first);
}
}
for (auto pageId : pagesToDelete) {
removePage(pageId);
}
auto waitForDebugger =
(inspectedContexts_->find(title) != inspectedContexts_->end());
return addPage(hermes::inspector_modern::chrome::CDPHandler::create(
std::move(adapter), title, waitForDebugger));
}
void ConnectionDemux::disableDebugging(DebugSessionToken session) {
std::scoped_lock lock(mutex_);
if (conns_.find(session) == conns_.end()) {
return;
}
removePage(session);
}
int ConnectionDemux::addPage(
std::shared_ptr<hermes::inspector_modern::chrome::CDPHandler> conn) {
auto connectFunc = [conn, this](std::unique_ptr<IRemoteConnection> remoteConn)
-> std::unique_ptr<ILocalConnection> {
// This cannot be unique_ptr as std::function is copyable but unique_ptr
// isn't. TODO: Change the CDPHandler API to accommodate this and not
// require a copyable callback?
std::shared_ptr<IRemoteConnection> sharedConn = std::move(remoteConn);
if (!conn->registerCallbacks(
[sharedConn](const std::string& message) {
sharedConn->onMessage(message);
},
[sharedConn]() { sharedConn->onDisconnect(); })) {
return nullptr;
}
return std::make_unique<LocalConnection>(conn, inspectedContexts_);
};
int pageId = globalInspector_.addPage(
conn->getTitle(), "Hermes", std::move(connectFunc));
conns_[pageId] = std::move(conn);
return pageId;
}
void ConnectionDemux::removePage(int pageId) {
globalInspector_.removePage(pageId);
auto conn = conns_.at(pageId);
std::string title = conn->getTitle();
inspectedContexts_->erase(title);
conn->unregisterCallbacks();
conns_.erase(pageId);
}
} // namespace chrome
} // namespace inspector_modern
} // namespace hermes
} // namespace facebook
#endif // HERMES_ENABLE_DEBUGGER

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#ifdef HERMES_ENABLE_DEBUGGER
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <hermes/hermes.h>
#include <hermes/inspector-modern/chrome/Registration.h>
#include <hermes/inspector/RuntimeAdapter.h>
#include <hermes/inspector/chrome/CDPHandler.h>
#include <jsinspector-modern/InspectorInterfaces.h>
namespace facebook {
namespace hermes {
namespace inspector_modern {
namespace chrome {
/*
* ConnectionDemux keeps track of all debuggable Hermes runtimes (called
* "pages" in the higher-level React Native API) in this process. See
* Registration.h for documentation of the public API.
*/
class ConnectionDemux {
public:
explicit ConnectionDemux(
facebook::react::jsinspector_modern::IInspector& inspector);
~ConnectionDemux();
ConnectionDemux(const ConnectionDemux&) = delete;
ConnectionDemux& operator=(const ConnectionDemux&) = delete;
DebugSessionToken enableDebugging(
std::unique_ptr<RuntimeAdapter> adapter,
const std::string& title);
void disableDebugging(DebugSessionToken session);
private:
int addPage(
std::shared_ptr<hermes::inspector_modern::chrome::CDPHandler> conn);
void removePage(int pageId);
facebook::react::jsinspector_modern::IInspector& globalInspector_;
std::mutex mutex_;
std::unordered_map<
int,
std::shared_ptr<hermes::inspector_modern::chrome::CDPHandler>>
conns_;
std::shared_ptr<std::unordered_set<std::string>> inspectedContexts_;
};
} // namespace chrome
} // namespace inspector_modern
} // namespace hermes
} // namespace facebook
#endif // HERMES_ENABLE_DEBUGGER

View File

@@ -0,0 +1,228 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "HermesRuntimeAgentDelegate.h"
// If HERMES_ENABLE_DEBUGGER isn't defined, we can't access any Hermes
// CDPHandler headers or types.
#ifdef HERMES_ENABLE_DEBUGGER
#include <hermes/inspector/RuntimeAdapter.h>
#include <hermes/inspector/chrome/CDPHandler.h>
#else // HERMES_ENABLE_DEBUGGER
#include <jsinspector-modern/FallbackRuntimeAgentDelegate.h>
#endif // HERMES_ENABLE_DEBUGGER
#include <hermes/hermes.h>
#include <jsinspector-modern/ReactCdp.h>
using namespace facebook::hermes;
namespace facebook::react::jsinspector_modern {
#ifdef HERMES_ENABLE_DEBUGGER
namespace {
/**
* An implementation of the Hermes RuntimeAdapter interface (part of
* Hermes's CDPHandler API) for use within a React Native RuntimeAgentDelegate.
*/
class HermesRuntimeAgentDelegateAdapter
: public hermes::inspector_modern::RuntimeAdapter {
public:
HermesRuntimeAgentDelegateAdapter(
std::shared_ptr<hermes::HermesRuntime> runtime,
RuntimeExecutor runtimeExecutor)
: runtime_(runtime), runtimeExecutor_(runtimeExecutor) {}
HermesRuntime& getRuntime() override {
return *runtime_;
}
void tickleJs() override {
runtimeExecutor_([](jsi::Runtime& runtime) {
jsi::Function func =
runtime.global().getPropertyAsFunction(runtime, "__tickleJs");
func.call(runtime);
});
}
private:
std::shared_ptr<hermes::HermesRuntime> runtime_;
RuntimeExecutor runtimeExecutor_;
};
} // namespace
/**
* A RuntimeAgentDelegate that handles requests from the Chrome DevTools
* Protocol for an instance of Hermes.
*/
class HermesRuntimeAgentDelegate::Impl final : public RuntimeAgentDelegate {
using HermesCDPHandler = hermes::inspector_modern::chrome::CDPHandler;
using HermesExecutionContextDescription =
hermes::inspector_modern::chrome::CDPHandlerExecutionContextDescription;
using HermesState = hermes::inspector_modern::chrome::State;
struct HermesStateWrapper : public ExportedState {
explicit HermesStateWrapper(std::unique_ptr<HermesState> state)
: state_(std::move(state)) {}
static std::unique_ptr<HermesState> unwrapDestructively(
ExportedState* wrapper) {
if (!wrapper) {
return nullptr;
}
if (auto* typedWrapper = dynamic_cast<HermesStateWrapper*>(wrapper)) {
return std::move(typedWrapper->state_);
}
return nullptr;
}
private:
std::unique_ptr<HermesState> state_;
};
public:
/**
* \param frontendChannel A channel used to send responses and events to the
* frontend.
* \param sessionState The state of the current CDP session. This will only
* be accessed on the main thread (during the constructor, in handleRequest,
* etc).
* \param previouslyExportedState The exported state from a previous instance
* of RuntimeAgentDelegate (NOT necessarily HermesRuntimeAgentDelegate). This
* may be nullptr, and if not nullptr it may be of any concrete type that
* implements RuntimeAgentDelegate::ExportedState.
* \param executionContextDescription A description of the execution context
* represented by this runtime. This is used for disambiguating the
* source/destination of CDP messages when there are multiple runtimes
* (concurrently or over the life of a Page).
* \param runtime The HermesRuntime that this agent is attached to.
* \param runtimeExecutor A callback for scheduling work on the JS thread.
* \c runtimeExecutor may drop scheduled work if the runtime is destroyed
* first.
*/
Impl(
FrontendChannel frontendChannel,
SessionState& sessionState,
std::unique_ptr<RuntimeAgentDelegate::ExportedState>
previouslyExportedState,
const ExecutionContextDescription& executionContextDescription,
std::shared_ptr<hermes::HermesRuntime> runtime,
RuntimeExecutor runtimeExecutor)
: hermes_(HermesCDPHandler::create(
std::make_unique<HermesRuntimeAgentDelegateAdapter>(
runtime,
runtimeExecutor),
/* waitForDebugger */ false,
/* enableConsoleAPICapturing */ false,
/* state */
HermesStateWrapper::unwrapDestructively(
previouslyExportedState.get()),
{.isRuntimeDomainEnabled = sessionState.isRuntimeDomainEnabled},
HermesExecutionContextDescription{
.id = executionContextDescription.id,
.origin = executionContextDescription.origin,
.name = executionContextDescription.name,
.auxData = std::nullopt,
.shouldSendNotifications = false})) {
hermes_->registerCallbacks(
/* msgCallback */
[frontendChannel =
std::move(frontendChannel)](const std::string& messageFromHermes) {
frontendChannel(messageFromHermes);
;
},
/* onUnregister */
[]() {});
}
/**
* Handle a CDP request. The response will be sent over the provided
* \c FrontendChannel synchronously or asynchronously.
* \param req The parsed request.
* \returns true if this agent has responded, or will respond asynchronously,
* to the request (with either a success or error message). False if the
* agent expects another agent to respond to the request instead.
*/
bool handleRequest(const cdp::PreparsedRequest& req) override {
// TODO: Change to string::starts_with when we're on C++20.
if (req.method.rfind("Log.", 0) == 0) {
// Since we know Hermes doesn't do anything useful with Log messages, but
// our containing PageAgent will, just bail out early.
// TODO: We need a way to negotiate this more dynamically with Hermes
// through the API.
return false;
}
// Forward everything else to Hermes's CDPHandler.
hermes_->handle(req.toJson());
// Let the call know that this request is handled (i.e. it is Hermes's
// responsibility to respond with either success or an error).
return true;
}
virtual std::unique_ptr<ExportedState> getExportedState() override {
return std::make_unique<HermesStateWrapper>(hermes_->getState());
}
private:
std::shared_ptr<HermesCDPHandler> hermes_;
};
#else // !HERMES_ENABLE_DEBUGGER
/**
* A stub for HermesRuntimeAgentDelegate when Hermes is compiled without
* debugging support.
*/
class HermesRuntimeAgentDelegate::Impl final
: public FallbackRuntimeAgentDelegate {
public:
Impl(
FrontendChannel frontendChannel,
SessionState& sessionState,
std::unique_ptr<RuntimeAgentDelegate::ExportedState>,
const ExecutionContextDescription&,
std::shared_ptr<hermes::HermesRuntime> runtime,
RuntimeExecutor)
: FallbackRuntimeAgentDelegate(
std::move(frontendChannel),
sessionState,
runtime->description()) {}
};
#endif // HERMES_ENABLE_DEBUGGER
HermesRuntimeAgentDelegate::HermesRuntimeAgentDelegate(
FrontendChannel frontendChannel,
SessionState& sessionState,
std::unique_ptr<RuntimeAgentDelegate::ExportedState>
previouslyExportedState,
const ExecutionContextDescription& executionContextDescription,
std::shared_ptr<hermes::HermesRuntime> runtime,
RuntimeExecutor runtimeExecutor)
: impl_(std::make_unique<Impl>(
std::move(frontendChannel),
sessionState,
std::move(previouslyExportedState),
executionContextDescription,
std::move(runtime),
std::move(runtimeExecutor))) {}
bool HermesRuntimeAgentDelegate::handleRequest(
const cdp::PreparsedRequest& req) {
return impl_->handleRequest(req);
}
std::unique_ptr<HermesRuntimeAgentDelegate::ExportedState>
HermesRuntimeAgentDelegate::getExportedState() {
return impl_->getExportedState();
}
} // namespace facebook::react::jsinspector_modern

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <ReactCommon/RuntimeExecutor.h>
#include <hermes/hermes.h>
#include <jsinspector-modern/ReactCdp.h>
namespace facebook::react::jsinspector_modern {
/**
* A RuntimeAgentDelegate that handles requests from the Chrome DevTools
* Protocol for an instance of Hermes.
*/
class HermesRuntimeAgentDelegate : public RuntimeAgentDelegate {
public:
/**
* \param frontendChannel A channel used to send responses and events to the
* frontend.
* \param sessionState The state of the current CDP session. This will only
* be accessed on the main thread (during the constructor, in handleRequest,
* etc).
* \param previouslyExportedState The exported state from a previous instance
* of RuntimeAgentDelegate (NOT necessarily HermesRuntimeAgentDelegate). This
* may be nullptr, and if not nullptr it may be of any concrete type that
* implements RuntimeAgentDelegate::ExportedState.
* \param executionContextDescription A description of the execution context
* represented by this runtime. This is used for disambiguating the
* source/destination of CDP messages when there are multiple runtimes
* (concurrently or over the life of a Page).
* \param runtime The HermesRuntime that this agent is attached to.
* \param runtimeExecutor A callback for scheduling work on the JS thread.
* \c runtimeExecutor may drop scheduled work if the runtime is destroyed
* first.
*/
HermesRuntimeAgentDelegate(
FrontendChannel frontendChannel,
SessionState& sessionState,
std::unique_ptr<RuntimeAgentDelegate::ExportedState>
previouslyExportedState,
const ExecutionContextDescription& executionContextDescription,
std::shared_ptr<hermes::HermesRuntime> runtime,
RuntimeExecutor runtimeExecutor);
/**
* Handle a CDP request. The response will be sent over the provided
* \c FrontendChannel synchronously or asynchronously.
* \param req The parsed request.
* \returns true if this agent has responded, or will respond asynchronously,
* to the request (with either a success or error message). False if the
* agent expects another agent to respond to the request instead.
*/
bool handleRequest(const cdp::PreparsedRequest& req) override;
virtual std::unique_ptr<ExportedState> getExportedState() override;
private:
// We use the private implementation idiom to keep HERMES_ENABLE_DEBUGGER
// checks out of the header.
class Impl;
const std::unique_ptr<Impl> impl_;
};
} // namespace facebook::react::jsinspector_modern

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "Registration.h"
#include "ConnectionDemux.h"
#ifdef HERMES_ENABLE_DEBUGGER
namespace facebook {
namespace hermes {
namespace inspector_modern {
namespace chrome {
namespace {
ConnectionDemux& demux() {
static ConnectionDemux instance{
facebook::react::jsinspector_modern::getInspectorInstance()};
return instance;
}
} // namespace
DebugSessionToken enableDebugging(
std::unique_ptr<RuntimeAdapter> adapter,
const std::string& title) {
return demux().enableDebugging(std::move(adapter), title);
}
void disableDebugging(DebugSessionToken session) {
demux().disableDebugging(session);
}
} // namespace chrome
} // namespace inspector_modern
} // namespace hermes
} // namespace facebook
#endif // HERMES_ENABLE_DEBUGGER

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#ifdef HERMES_ENABLE_DEBUGGER
#include <memory>
#include <string>
#include <hermes/hermes.h>
#include <hermes/inspector/RuntimeAdapter.h>
namespace facebook {
namespace hermes {
namespace inspector_modern {
namespace chrome {
using DebugSessionToken = int;
/*
* enableDebugging adds this runtime to the list of debuggable JS targets
* (called "pages" in the higher-level React Native API) in this process. It
* should be called before any JS runs in the runtime. The returned token
* can be used to disable debugging for this runtime.
*/
extern DebugSessionToken enableDebugging(
std::unique_ptr<RuntimeAdapter> adapter,
const std::string& title);
/*
* disableDebugging removes this runtime from the list of debuggable JS targets
* in this process. The runtime to remove is identified by the token returned
* from enableDebugging.
*/
extern void disableDebugging(DebugSessionToken session);
} // namespace chrome
} // namespace inspector_modern
} // namespace hermes
} // namespace facebook
#endif // HERMES_ENABLE_DEBUGGER

View File

@@ -0,0 +1,148 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
#include <gtest/gtest.h>
#include <hermes/hermes.h>
#include <hermes/inspector-modern/chrome/ConnectionDemux.h>
#include <jsinspector-modern/InspectorInterfaces.h>
namespace facebook {
namespace hermes {
namespace inspector_modern {
namespace chrome {
using ::facebook::react::jsinspector_modern::IInspector;
using ::facebook::react::jsinspector_modern::InspectorPageDescription;
using ::facebook::react::jsinspector_modern::IRemoteConnection;
namespace {
std::unordered_map<int, std::string> makePageMap(
const std::vector<InspectorPageDescription>& pages) {
std::unordered_map<int, std::string> pageMap;
for (auto& page : pages) {
pageMap[page.id] = page.title;
}
return pageMap;
}
void expectPages(
IInspector& inspector,
const std::unordered_map<int, std::string>& expected) {
auto pages = makePageMap(inspector.getPages());
EXPECT_EQ(pages, expected);
}
class TestRemoteConnection : public IRemoteConnection {
public:
class Data {
public:
void expectDisconnected() {
std::unique_lock<std::mutex> lock(mutex_);
cv_.wait_for(
lock, std::chrono::milliseconds(2500), [&] { return !connected_; });
EXPECT_FALSE(connected_);
}
void setDisconnected() {
std::scoped_lock lock(mutex_);
connected_ = false;
cv_.notify_one();
}
private:
std::mutex mutex_;
std::condition_variable cv_;
bool connected_{true};
};
TestRemoteConnection() : data_(std::make_shared<Data>()) {}
~TestRemoteConnection() {}
void onMessage(std::string message) override {}
void onDisconnect() override {
data_->setDisconnected();
}
std::shared_ptr<Data> getData() {
return data_;
}
private:
std::shared_ptr<Data> data_;
};
}; // namespace
TEST(ConnectionDemuxTests, TestEnableDisable) {
std::shared_ptr<HermesRuntime> runtime1(
facebook::hermes::makeHermesRuntime());
std::shared_ptr<HermesRuntime> runtime2(
facebook::hermes::makeHermesRuntime());
auto inspector =
facebook::react::jsinspector_modern::makeTestInspectorInstance();
ConnectionDemux demux{*inspector};
int id1 = demux.enableDebugging(
std::make_unique<SharedRuntimeAdapter>(runtime1), "page1");
int id2 = demux.enableDebugging(
std::make_unique<SharedRuntimeAdapter>(runtime2), "page2");
expectPages(*inspector, {{id1, "page1"}, {id2, "page2"}});
auto remoteConn1 = std::make_unique<TestRemoteConnection>();
auto remoteData1 = remoteConn1->getData();
auto localConn1 = inspector->connect(id1, std::move(remoteConn1));
EXPECT_NE(localConn1.get(), nullptr);
{
// If we connect to the same page id again without disconnecting, we should
// get null
auto remoteConn = std::make_unique<TestRemoteConnection>();
auto localConn = inspector->connect(id1, std::move(remoteConn));
EXPECT_EQ(localConn.get(), nullptr);
}
auto remoteConn2 = std::make_unique<TestRemoteConnection>();
auto remoteData2 = remoteConn2->getData();
auto localConn2 = inspector->connect(id2, std::move(remoteConn2));
EXPECT_NE(localConn2.get(), nullptr);
// Disable debugging on runtime2. This should remove its page from the list
// and call onDisconnect on its remoteConn
demux.disableDebugging(id2);
expectPages(*inspector, {{id1, "page1"}});
remoteData2->expectDisconnected();
// Disconnect conn1. Its page should still be in the page list and
// onDisconnect should be called.
localConn1->disconnect();
remoteData1->expectDisconnected();
{
// Should still be able to reconnect after disconnecting
auto remoteConn = std::make_unique<TestRemoteConnection>();
auto localConn = inspector->connect(id1, std::move(remoteConn));
EXPECT_NE(localConn.get(), nullptr);
}
}
} // namespace chrome
} // namespace inspector_modern
} // namespace hermes
} // namespace facebook