mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Implement core Network CDP events on iOS (#50142)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/50142 This is a first pass at integrating `NetworkReporter` in our networking stack on iOS (`RCTNetworking.mm`). **Implemented events** Wires up minimal events sufficient to populate the Chrome DevTools Network panel: - `Network.requestWillBeSent` - `Network.responseReceived` - `Network.loadingFinished` **Other notes** `RCTNetworking` is used (tentatively) as the integration point since it: - Is the default implementation for the network stack on iOS. - Should allow us to pair with originating JS call site down the line. - Intercepts Blob requests (at least `RCTImageLoader`). - Sits outside the user-configurable `RCTNetworkingResponseHandler` and `RCTNetworkingRequestHandler` concepts. - Is where network events are currently sent to JavaScript (`sendEventWithName`). NOTE: Reminder: `NetworkReporter` is currently a no-op without the `fuseboxNetworkInspectionEnabled` experiment set. Changelog: [Internal] Reviewed By: javache Differential Revision: D71470038 fbshipit-source-id: 069d77473c333a98f796b3dffa670a39b3016b2b
This commit is contained in:
committed by
Facebook GitHub Bot
parent
bcce235841
commit
793094fc0f
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* [Experimental] An interface for reporting network events to the modern
|
||||
* debugger server and Web Performance APIs.
|
||||
*
|
||||
* This is a helper class wrapping
|
||||
* `facebook::react::jsinspector_modern::NetworkReporter`.
|
||||
*/
|
||||
@interface RCTInspectorNetworkReporter : NSObject
|
||||
|
||||
+ (void)reportRequestStart:(NSNumber *)requestId
|
||||
request:(NSURLRequest *)request
|
||||
encodedDataLength:(int)encodedDataLength;
|
||||
+ (void)reportResponseStart:(NSNumber *)requestId
|
||||
response:(NSURLResponse *)response
|
||||
statusCode:(int)statusCode
|
||||
headers:(NSDictionary<NSString *, NSString *> *)headers;
|
||||
+ (void)reportResponseEnd:(NSNumber *)requestId encodedDataLength:(int)encodedDataLength;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import "RCTInspectorNetworkReporter.h"
|
||||
|
||||
#import <jsinspector-modern/network/NetworkReporter.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace facebook::react::jsinspector_modern;
|
||||
|
||||
Headers convertNSDictionaryToHeaders(const NSDictionary<NSString *, NSString *> *headers)
|
||||
{
|
||||
Headers responseHeaders;
|
||||
for (NSString *key in headers) {
|
||||
responseHeaders[[key UTF8String]] = [headers[key] UTF8String];
|
||||
}
|
||||
return responseHeaders;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@implementation RCTInspectorNetworkReporter {
|
||||
}
|
||||
|
||||
+ (void)reportRequestStart:(NSNumber *)requestId
|
||||
request:(NSURLRequest *)request
|
||||
encodedDataLength:(int)encodedDataLength
|
||||
{
|
||||
RequestInfo requestInfo;
|
||||
requestInfo.url = [request.URL absoluteString].UTF8String;
|
||||
requestInfo.httpMethod = [request.HTTPMethod UTF8String];
|
||||
requestInfo.headers = convertNSDictionaryToHeaders(request.allHTTPHeaderFields);
|
||||
requestInfo.httpBody = std::string((const char *)request.HTTPBody.bytes, request.HTTPBody.length);
|
||||
|
||||
NetworkReporter::getInstance().reportRequestStart(
|
||||
requestId.stringValue.UTF8String, requestInfo, encodedDataLength, std::nullopt);
|
||||
}
|
||||
|
||||
+ (void)reportResponseStart:(NSNumber *)requestId
|
||||
response:(NSURLResponse *)response
|
||||
statusCode:(int)statusCode
|
||||
headers:(NSDictionary<NSString *, NSString *> *)headers
|
||||
{
|
||||
ResponseInfo responseInfo;
|
||||
responseInfo.url = response.URL.absoluteString.UTF8String;
|
||||
responseInfo.statusCode = statusCode;
|
||||
responseInfo.headers = convertNSDictionaryToHeaders(headers);
|
||||
|
||||
NetworkReporter::getInstance().reportResponseStart(
|
||||
requestId.stringValue.UTF8String, responseInfo, response.expectedContentLength);
|
||||
}
|
||||
|
||||
+ (void)reportResponseEnd:(NSNumber *)requestId encodedDataLength:(int)encodedDataLength
|
||||
{
|
||||
NetworkReporter::getInstance().reportResponseEnd(requestId.stringValue.UTF8String, encodedDataLength);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#import <React/RCTHTTPRequestHandler.h>
|
||||
|
||||
#import "RCTInspectorNetworkReporter.h"
|
||||
#import "RCTNetworkPlugins.h"
|
||||
|
||||
static BOOL gEnableNetworkingRequestQueue = NO;
|
||||
@@ -587,16 +588,22 @@ RCT_EXPORT_MODULE()
|
||||
RCTURLRequestResponseBlock responseBlock = ^(NSURLResponse *response) {
|
||||
NSDictionary<NSString *, NSString *> *headers;
|
||||
NSInteger status;
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) { // Might be a local file request
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||
headers = httpResponse.allHeaderFields ?: @{};
|
||||
status = httpResponse.statusCode;
|
||||
} else {
|
||||
// Other HTTP-like request
|
||||
headers = response.MIMEType ? @{@"Content-Type" : response.MIMEType} : @{};
|
||||
status = 200;
|
||||
}
|
||||
id responseURL = response.URL ? response.URL.absoluteString : [NSNull null];
|
||||
NSArray<id> *responseJSON = @[ task.requestID, @(status), headers, responseURL ];
|
||||
|
||||
[RCTInspectorNetworkReporter reportResponseStart:task.requestID
|
||||
response:response
|
||||
statusCode:status
|
||||
headers:headers];
|
||||
[weakSelf sendEventWithName:@"didReceiveNetworkResponse" body:responseJSON];
|
||||
};
|
||||
|
||||
@@ -655,6 +662,7 @@ RCT_EXPORT_MODULE()
|
||||
NSArray *responseJSON =
|
||||
@[ task.requestID, RCTNullIfNil(error.localizedDescription), error.code == kCFURLErrorTimedOut ? @YES : @NO ];
|
||||
|
||||
[RCTInspectorNetworkReporter reportResponseEnd:task.requestID encodedDataLength:data.length];
|
||||
[strongSelf sendEventWithName:@"didCompleteNetworkResponse" body:responseJSON];
|
||||
[strongSelf->_tasksByRequestID removeObjectForKey:task.requestID];
|
||||
};
|
||||
@@ -673,6 +681,9 @@ RCT_EXPORT_MODULE()
|
||||
responseSender(@[ task.requestID ]);
|
||||
}
|
||||
|
||||
[RCTInspectorNetworkReporter reportRequestStart:task.requestID
|
||||
request:request
|
||||
encodedDataLength:task.response.expectedContentLength];
|
||||
[task start];
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ Pod::Spec.new do |s|
|
||||
|
||||
add_dependency(s, "React-RCTFBReactNativeSpec")
|
||||
add_dependency(s, "ReactCommon", :subspec => "turbomodule/core", :additional_framework_paths => ["react/nativemodule/core"])
|
||||
add_dependency(s, "React-jsinspectorcdp", :framework_name => 'jsinspector_moderncdp')
|
||||
add_dependency(s, "React-jsinspectornetwork", :framework_name => 'jsinspector_modernnetwork')
|
||||
add_dependency(s, "React-NativeModulesApple", :additional_framework_paths => ["build/generated/ios"])
|
||||
|
||||
add_rn_third_party_dependencies(s)
|
||||
|
||||
@@ -86,6 +86,7 @@ Pod::Spec.new do |s|
|
||||
add_dependency(s, "React-RCTAnimation", :framework_name => 'RCTAnimation')
|
||||
add_dependency(s, "React-jsinspector", :framework_name => 'jsinspector_modern')
|
||||
add_dependency(s, "React-jsinspectorcdp", :framework_name => 'jsinspector_moderncdp')
|
||||
add_dependency(s, "React-jsinspectornetwork", :framework_name => 'jsinspector_modernnetwork')
|
||||
add_dependency(s, "React-jsinspectortracing", :framework_name => 'jsinspector_moderntracing')
|
||||
add_dependency(s, "React-renderercss")
|
||||
|
||||
|
||||
@@ -271,16 +271,19 @@ bool NetworkIOAgent::handleRequest(
|
||||
}
|
||||
|
||||
if (InspectorFlags::getInstance().getNetworkInspectionEnabled()) {
|
||||
auto& networkReporter = NetworkReporter::getInstance();
|
||||
|
||||
// @cdp Network.enable support is experimental.
|
||||
if (req.method == "Network.enable") {
|
||||
NetworkReporter::getInstance().enableDebugging();
|
||||
networkReporter.setFrontendChannel(frontendChannel_);
|
||||
networkReporter.enableDebugging();
|
||||
frontendChannel_(cdp::jsonResult(req.id));
|
||||
return true;
|
||||
}
|
||||
|
||||
// @cdp Network.disable support is experimental.
|
||||
if (req.method == "Network.disable") {
|
||||
NetworkReporter::getInstance().disableDebugging();
|
||||
networkReporter.disableDebugging();
|
||||
frontendChannel_(cdp::jsonResult(req.id));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -24,4 +24,5 @@ target_include_directories(jsinspector_network PUBLIC ${REACT_COMMON_DIR})
|
||||
target_link_libraries(jsinspector_network
|
||||
folly_runtime
|
||||
glog
|
||||
jsinspector_cdp
|
||||
)
|
||||
|
||||
@@ -8,16 +8,110 @@
|
||||
#include "NetworkReporter.h"
|
||||
|
||||
#include <glog/logging.h>
|
||||
#include <jsinspector-modern/cdp/CdpJson.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace facebook::react::jsinspector_modern {
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Get the CDP `ResourceType` for a given MIME type.
|
||||
*
|
||||
* https://chromedevtools.github.io/devtools-protocol/tot/Network/#type-ResourceType
|
||||
*/
|
||||
std::string mimeTypeToResourceType(const std::string& mimeType) {
|
||||
if (mimeType.find("image/") == 0) {
|
||||
return "Image";
|
||||
}
|
||||
|
||||
if (mimeType.find("video/") == 0 || mimeType.find("audio/") == 0) {
|
||||
return "Media";
|
||||
}
|
||||
|
||||
if (mimeType == "application/javascript" || mimeType == "text/javascript" ||
|
||||
mimeType == "application/x-javascript") {
|
||||
return "Script";
|
||||
}
|
||||
|
||||
if (mimeType == "application/json" || mimeType.find("application/xml") == 0 ||
|
||||
mimeType == "text/xml") {
|
||||
// Assume XHR for JSON/XML types
|
||||
return "XHR";
|
||||
}
|
||||
|
||||
return "Other";
|
||||
}
|
||||
|
||||
folly::dynamic headersToDynamic(const std::optional<Headers>& headers) {
|
||||
folly::dynamic result = folly::dynamic::object;
|
||||
|
||||
if (headers) {
|
||||
for (const auto& [key, value] : *headers) {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
folly::dynamic requestToCdpParams(const RequestInfo& request) {
|
||||
folly::dynamic result = folly::dynamic::object;
|
||||
result["url"] = request.url;
|
||||
result["method"] = request.httpMethod;
|
||||
result["headers"] = headersToDynamic(request.headers);
|
||||
result["postData"] = request.httpBody.value();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
folly::dynamic responseToCdpParams(
|
||||
const ResponseInfo& response,
|
||||
int encodedDataLength) {
|
||||
auto headers = response.headers.value_or(Headers());
|
||||
std::string mimeType = "Other";
|
||||
|
||||
if (headers.find("Content-Type") != headers.end()) {
|
||||
mimeType = mimeTypeToResourceType(headers.at("Content-Type"));
|
||||
}
|
||||
|
||||
folly::dynamic result = folly::dynamic::object;
|
||||
result["url"] = response.url;
|
||||
result["status"] = response.statusCode;
|
||||
result["statusText"] = "";
|
||||
result["headers"] = headersToDynamic(response.headers);
|
||||
result["mimeType"] = mimeType;
|
||||
result["encodedDataLength"] = encodedDataLength;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current Unix timestamp in seconds (µs precision).
|
||||
*/
|
||||
double getCurrentUnixTimestampSeconds() {
|
||||
auto now = std::chrono::system_clock::now().time_since_epoch();
|
||||
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(now).count();
|
||||
auto micros =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(now).count() %
|
||||
1000000;
|
||||
|
||||
return static_cast<double>(seconds) +
|
||||
(static_cast<double>(micros) / 1000000.0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NetworkReporter& NetworkReporter::getInstance() {
|
||||
static NetworkReporter tracer;
|
||||
return tracer;
|
||||
}
|
||||
|
||||
void NetworkReporter::setFrontendChannel(FrontendChannel frontendChannel) {
|
||||
frontendChannel_ = std::move(frontendChannel);
|
||||
}
|
||||
|
||||
bool NetworkReporter::enableDebugging() {
|
||||
if (debuggingEnabled_.load(std::memory_order_acquire)) {
|
||||
return false;
|
||||
@@ -38,13 +132,34 @@ bool NetworkReporter::disableDebugging() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void NetworkReporter::reportRequestStart(const std::string& /*requestId*/) {
|
||||
void NetworkReporter::reportRequestStart(
|
||||
const std::string& requestId,
|
||||
const RequestInfo& requestInfo,
|
||||
int encodedDataLength,
|
||||
const std::optional<ResponseInfo>& redirectResponse) {
|
||||
if (!debuggingEnabled_.load(std::memory_order_relaxed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(T216933356)
|
||||
throw std::runtime_error("Not implemented");
|
||||
double timestamp = getCurrentUnixTimestampSeconds();
|
||||
|
||||
folly::dynamic params = folly::dynamic::object;
|
||||
params["requestId"] = requestId;
|
||||
params["loaderId"] = "";
|
||||
params["documentURL"] = "mobile";
|
||||
params["request"] = requestToCdpParams(requestInfo);
|
||||
// NOTE: timestamp and wallTime share the same time unit and precision,
|
||||
// except wallTime is from an arbitrary epoch - use the Unix epoch for both.
|
||||
params["timestamp"] = timestamp;
|
||||
params["wallTime"] = timestamp;
|
||||
params["initiator"] = folly::dynamic::object("type", "script");
|
||||
params["redirectHasExtraInfo"] = redirectResponse.has_value();
|
||||
if (redirectResponse.has_value()) {
|
||||
params["redirectResponse"] =
|
||||
responseToCdpParams(redirectResponse.value(), encodedDataLength);
|
||||
}
|
||||
|
||||
frontendChannel_(cdp::jsonNotification("Network.requestWillBeSent", params));
|
||||
}
|
||||
|
||||
void NetworkReporter::reportConnectionTiming(const std::string& /*requestId*/) {
|
||||
@@ -65,13 +180,26 @@ void NetworkReporter::reportRequestFailed(const std::string& /*requestId*/) {
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
void NetworkReporter::reportResponseStart(const std::string& /*requestId*/) {
|
||||
void NetworkReporter::reportResponseStart(
|
||||
const std::string& requestId,
|
||||
const ResponseInfo& responseInfo,
|
||||
int encodedDataLength) {
|
||||
if (!debuggingEnabled_.load(std::memory_order_relaxed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(T216933356)
|
||||
throw std::runtime_error("Not implemented");
|
||||
folly::dynamic responseParams =
|
||||
responseToCdpParams(responseInfo, encodedDataLength);
|
||||
|
||||
folly::dynamic params = folly::dynamic::object;
|
||||
params["requestId"] = requestId;
|
||||
params["loaderId"] = "";
|
||||
params["timestamp"] = getCurrentUnixTimestampSeconds();
|
||||
params["type"] = responseParams["mimeType"];
|
||||
params["response"] = responseParams;
|
||||
params["hasExtraInfo"] = false;
|
||||
|
||||
frontendChannel_(cdp::jsonNotification("Network.responseReceived", params));
|
||||
}
|
||||
|
||||
void NetworkReporter::reportDataReceived(const std::string& /*requestId*/) {
|
||||
@@ -83,13 +211,19 @@ void NetworkReporter::reportDataReceived(const std::string& /*requestId*/) {
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
void NetworkReporter::reportResponseEnd(const std::string& /*requestId*/) {
|
||||
void NetworkReporter::reportResponseEnd(
|
||||
const std::string& requestId,
|
||||
int encodedDataLength) {
|
||||
if (!debuggingEnabled_.load(std::memory_order_relaxed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(T216933356)
|
||||
throw std::runtime_error("Not implemented");
|
||||
folly::dynamic params = folly::dynamic::object;
|
||||
params["requestId"] = requestId;
|
||||
params["timestamp"] = getCurrentUnixTimestampSeconds();
|
||||
params["encodedDataLength"] = encodedDataLength;
|
||||
|
||||
frontendChannel_(cdp::jsonNotification("Network.loadingFinished", params));
|
||||
}
|
||||
|
||||
} // namespace facebook::react::jsinspector_modern
|
||||
|
||||
@@ -8,10 +8,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace facebook::react::jsinspector_modern {
|
||||
|
||||
/**
|
||||
* A callback that can be used to send debugger messages (method responses and
|
||||
* events) to the frontend. The message must be a JSON-encoded string.
|
||||
* The callback may be called from any thread.
|
||||
*/
|
||||
using FrontendChannel = std::function<void(std::string_view messageJson)>;
|
||||
|
||||
using Headers = std::map<std::string, std::string>;
|
||||
|
||||
/**
|
||||
* Request info from the request caller.
|
||||
*/
|
||||
struct RequestInfo {
|
||||
std::string url;
|
||||
std::string httpMethod;
|
||||
std::optional<Headers> headers;
|
||||
std::optional<std::string> httpBody;
|
||||
};
|
||||
|
||||
/**
|
||||
* Response info from the request caller.
|
||||
*/
|
||||
struct ResponseInfo {
|
||||
std::string url;
|
||||
uint16_t statusCode;
|
||||
std::optional<Headers> headers;
|
||||
};
|
||||
|
||||
/**
|
||||
* [Experimental] An interface for reporting network events to the modern
|
||||
* debugger server and Web Performance APIs.
|
||||
@@ -20,6 +51,12 @@ class NetworkReporter {
|
||||
public:
|
||||
static NetworkReporter& getInstance();
|
||||
|
||||
/**
|
||||
* Set the channel used to send CDP events to the frontend. This should be
|
||||
* supplied before calling `enableDebugging`.
|
||||
*/
|
||||
void setFrontendChannel(FrontendChannel frontendChannel);
|
||||
|
||||
/**
|
||||
* Enable network tracking over CDP. Once enabled, network events will be
|
||||
* sent to the debugger client. Returns `false` if already enabled.
|
||||
@@ -45,7 +82,11 @@ class NetworkReporter {
|
||||
*
|
||||
* https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-requeststart
|
||||
*/
|
||||
void reportRequestStart(const std::string& requestId);
|
||||
void reportRequestStart(
|
||||
const std::string& requestId,
|
||||
const RequestInfo& requestInfo,
|
||||
int encodedDataLength,
|
||||
const std::optional<ResponseInfo>& redirectResponse);
|
||||
|
||||
/**
|
||||
* Report detailed timing info, such as DNS lookup, when a request has
|
||||
@@ -75,7 +116,10 @@ class NetworkReporter {
|
||||
*
|
||||
* https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responsestart
|
||||
*/
|
||||
void reportResponseStart(const std::string& requestId);
|
||||
void reportResponseStart(
|
||||
const std::string& requestId,
|
||||
const ResponseInfo& responseInfo,
|
||||
int encodedDataLength);
|
||||
|
||||
/**
|
||||
* Report when additional chunks of the response body have been received.
|
||||
@@ -93,9 +137,11 @@ class NetworkReporter {
|
||||
*
|
||||
* https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responseend
|
||||
*/
|
||||
void reportResponseEnd(const std::string& requestId);
|
||||
void reportResponseEnd(const std::string& requestId, int encodedDataLength);
|
||||
|
||||
private:
|
||||
FrontendChannel frontendChannel_;
|
||||
|
||||
NetworkReporter() = default;
|
||||
NetworkReporter(const NetworkReporter&) = delete;
|
||||
NetworkReporter& operator=(const NetworkReporter&) = delete;
|
||||
|
||||
+2
@@ -46,5 +46,7 @@ Pod::Spec.new do |s|
|
||||
s.header_mappings_dir = "../.."
|
||||
end
|
||||
|
||||
add_dependency(s, "React-jsinspectorcdp", :framework_name => 'jsinspector_moderncdp')
|
||||
|
||||
add_rn_third_party_dependencies(s)
|
||||
end
|
||||
|
||||
@@ -1706,6 +1706,7 @@ PODS:
|
||||
- glog
|
||||
- RCT-Folly
|
||||
- RCT-Folly/Fabric
|
||||
- React-jsinspectorcdp
|
||||
- SocketRocket
|
||||
- React-jsinspectortracing (1000.0.0):
|
||||
- boost
|
||||
@@ -1903,6 +1904,7 @@ PODS:
|
||||
- React-jsi
|
||||
- React-jsinspector
|
||||
- React-jsinspectorcdp
|
||||
- React-jsinspectornetwork
|
||||
- React-jsinspectortracing
|
||||
- React-performancetimeline
|
||||
- React-RCTAnimation
|
||||
@@ -1967,6 +1969,8 @@ PODS:
|
||||
- RCTTypeSafety
|
||||
- React-Core/RCTNetworkHeaders
|
||||
- React-jsi
|
||||
- React-jsinspectorcdp
|
||||
- React-jsinspectornetwork
|
||||
- React-NativeModulesApple
|
||||
- React-RCTFBReactNativeSpec
|
||||
- ReactCommon
|
||||
@@ -2594,7 +2598,7 @@ SPEC CHECKSUMS:
|
||||
React-jsiexecutor: 569425f7cd2c3e005a17e5211843e541c11d6916
|
||||
React-jsinspector: 885e8180e898f07e4d7df29e2681a89e69d736d3
|
||||
React-jsinspectorcdp: 5fb266e5f23d3a2819ba848e9d4d0b6b00f95934
|
||||
React-jsinspectornetwork: 79fbc86b7c308aeb29e883177b8573d953f6b5e2
|
||||
React-jsinspectornetwork: 207422b56a7918e83c94c207570849f83ab9052a
|
||||
React-jsinspectortracing: 80e9418ac67630c76f15ef06534087037a822330
|
||||
React-jsitooling: b1af12acbfcb039bf87f8f2a82bde3a79c2ddade
|
||||
React-jsitracing: ce443686f52538d1033ce7db1e7d643e866262f0
|
||||
@@ -2609,11 +2613,11 @@ SPEC CHECKSUMS:
|
||||
React-RCTAnimation: 263593e66c89bf810604b1ace15dfa382a1ca2df
|
||||
React-RCTAppDelegate: 3d35d7226338009b22d1cf9621eaa827acb8fd1d
|
||||
React-RCTBlob: 7b76230c53fe87d305eeeb250b0aae031bb6cbae
|
||||
React-RCTFabric: b3746cb91560703c95028002fb6db618aebb3b61
|
||||
React-RCTFabric: 78b6aec5985ef438a9c088032c0e89d2bd77b3ba
|
||||
React-RCTFBReactNativeSpec: 503491a0584dc29f03ef9f8ed366794604cd59ef
|
||||
React-RCTImage: de404b6b0ebe53976a97e3a0dee819c83e12977b
|
||||
React-RCTLinking: 06742cfad41c506091403a414370743a4ed75af3
|
||||
React-RCTNetwork: 091d9c4c3e24f0800d782d0c2a0077419b478d66
|
||||
React-RCTNetwork: f8f7ac330958ac85300827267390bc8decc06ba0
|
||||
React-RCTPushNotification: ea11178d499696516e0ff9ae335edbe99b06f94b
|
||||
React-RCTRuntime: 07b41aed797e8d950ada851c6363ecf931335663
|
||||
React-RCTSettings: d3c2dd305ec81f7faf42762ec598d57f07fd43be
|
||||
|
||||
Reference in New Issue
Block a user