@react-native-ohos/react-native-reanimated 3.18.3-beta.2 → 3.18.4-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,248 @@
1
+ // Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved
2
+ // Use of this source code is governed by a MIT license that can be
3
+ // found in the LICENSE file.
4
+
5
+ #include "ReanimatedSensor.h"
6
+ #include "glog/logging.h"
7
+ #include <memory>
8
+ #include <vector>
9
+ #include <sensors/oh_sensor.h>
10
+ #include <window_manager/oh_display_info.h>
11
+ #include <window_manager/oh_display_manager.h>
12
+ namespace rnoh {
13
+ static std::unordered_map<Sensor_Type, ReanimatedSensor *> g_sensorSubscriberMap_;
14
+ static std::mutex g_subscriberMapMutex_; // 保护映射表的互斥锁
15
+
16
+ void SensorDataCallbackImpl(Sensor_Event *event) {
17
+ if (event == nullptr) {
18
+ return;
19
+ }
20
+ int64_t timestamp = -1;
21
+ // 获取传感器数据的时间戳。
22
+ int32_t ret = OH_SensorEvent_GetTimestamp(event, &timestamp);
23
+ if (ret != SENSOR_SUCCESS) {
24
+ return;
25
+ }
26
+ Sensor_Type sensorType;
27
+ // 获取传感器类型。
28
+ ret = OH_SensorEvent_GetType(event, &sensorType);
29
+ if (ret != SENSOR_SUCCESS) {
30
+ return;
31
+ }
32
+ Sensor_Accuracy accuracy = SENSOR_ACCURACY_UNRELIABLE;
33
+ // 获取传感器数据的精度。
34
+ ret = OH_SensorEvent_GetAccuracy(event, &accuracy);
35
+ if (ret != SENSOR_SUCCESS) {
36
+ return;
37
+ }
38
+ float *data = nullptr;
39
+ uint32_t length = 0;
40
+ // 获取传感器数据。
41
+ ret = OH_SensorEvent_GetData(event, &data, &length);
42
+ if (ret != SENSOR_SUCCESS || data == nullptr) {
43
+ return;
44
+ }
45
+
46
+ std::lock_guard<std::mutex> lock(g_subscriberMapMutex_);
47
+ auto it = g_sensorSubscriberMap_.find(sensorType);
48
+ if (it == g_sensorSubscriberMap_.end()) {
49
+ return;
50
+ }
51
+ ReanimatedSensor *sensorInstance = it->second;
52
+ if (sensorInstance == nullptr) {
53
+ return;
54
+ }
55
+
56
+ switch (sensorType) {
57
+ case SENSOR_TYPE_ACCELEROMETER:
58
+ case SENSOR_TYPE_GYROSCOPE:
59
+ case SENSOR_TYPE_GRAVITY:
60
+ case SENSOR_TYPE_MAGNETIC_FIELD: {
61
+ double sendData[] = {
62
+ data[0],
63
+ data[1],
64
+ data[2],
65
+ };
66
+ // 传入 count,由 runSensor 拷贝到堆,避免跨线程 use-after-free
67
+ sensorInstance->runSensor(sendData, 3);
68
+ return;
69
+ }
70
+ case SENSOR_TYPE_ROTATION_VECTOR: {
71
+ double sendData[] = {
72
+ data[0], // x
73
+ data[1], // y
74
+ data[2], // z
75
+ data[3], // w
76
+ // ios
77
+ 0, // yaw
78
+ 0, // pitch
79
+ 0 // roll
80
+ };
81
+ sensorInstance->runSensor(sendData, 7);
82
+ return;
83
+ }
84
+ default:
85
+ return;
86
+ }
87
+ }
88
+
89
+
90
+ rnoh::ReanimatedSensor::ReanimatedSensor(int sensorType, int interval, int iosReferenceFrame,
91
+ std::function<void(double *, int)> setter,
92
+ std::function<void(TaskExecutor::Task &&)> runOnTheMainThread)
93
+ : iosReferenceFrame_(iosReferenceFrame), setter_(std::move(setter)),
94
+ runOnTheMainThread_(std::move(runOnTheMainThread)) {
95
+ if (interval == -1) {
96
+ uint32_t refreshRate = 0;
97
+ OH_NativeDisplayManager_GetDefaultDisplayRefreshRate(&refreshRate);
98
+ if (refreshRate > 0) {
99
+ interval_ = refreshRate * 1000000; // 120hz;
100
+ } else {
101
+ interval_ = 200000000;
102
+ }
103
+ } else {
104
+ // js unit is milliseconds
105
+ interval_ = interval * 1000000;
106
+ }
107
+ castSensorType(sensorType);
108
+ }
109
+
110
+
111
+ void rnoh::ReanimatedSensor::castSensorType(int sensorType) {
112
+ if (sensorType == 1) {
113
+ sensorType_ = SENSOR_TYPE_ACCELEROMETER;
114
+ } else if (sensorType == 2) {
115
+ sensorType_ = SENSOR_TYPE_GYROSCOPE;
116
+ } else if (sensorType == 3) {
117
+ sensorType_ = SENSOR_TYPE_GRAVITY;
118
+ } else if (sensorType == 4) {
119
+ sensorType_ = SENSOR_TYPE_MAGNETIC_FIELD;
120
+ } else if (sensorType == 5) {
121
+ sensorType_ = SENSOR_TYPE_ROTATION_VECTOR;
122
+ } else {
123
+ throw std::invalid_argument("[ReanimatedSensor] Unknown sensorType: " + sensorType);
124
+ }
125
+ }
126
+
127
+ bool rnoh::ReanimatedSensor::initialize() {
128
+ if (!isSupportSensor()) {
129
+ LOG(ERROR) << "[ReanimatedSensor] is not support, sensorType:" << sensorType_;
130
+ return false;
131
+ }
132
+ id_ = OH_Sensor_CreateSubscriptionId();
133
+ if (id_ == nullptr) {
134
+ LOG(ERROR) << "[ReanimatedSensor] create sensor id failed";
135
+ return false;
136
+ }
137
+ int32_t res = OH_SensorSubscriptionId_SetType(id_, sensorType_);
138
+ if (res != SENSOR_SUCCESS) {
139
+ LOG(ERROR) << "[ReanimatedSensor] set sensor type failed ,sensorType:" << sensorType_;
140
+ return false;
141
+ }
142
+ auto attr = OH_Sensor_CreateSubscriptionAttribute();
143
+ if (attr == nullptr) {
144
+ LOG(ERROR) << "[ReanimatedSensor] create sensor attribute failed";
145
+ return false;
146
+ }
147
+
148
+ res = OH_SensorSubscriptionAttribute_SetSamplingInterval(attr, interval_);
149
+ if (res != SENSOR_SUCCESS) {
150
+ LOG(ERROR) << "[ReanimatedSensor] set sensor Interval failed, interval_:" << interval_;
151
+ return false;
152
+ }
153
+
154
+ subSensor_ = OH_Sensor_CreateSubscriber();
155
+
156
+ {
157
+ std::lock_guard<std::mutex> lock(g_subscriberMapMutex_);
158
+ g_sensorSubscriberMap_[sensorType_] = this;
159
+ }
160
+
161
+ int32_t ret = OH_SensorSubscriber_SetCallback(subSensor_, SensorDataCallbackImpl);
162
+ if (ret != SENSOR_SUCCESS) {
163
+ LOG(ERROR) << "[ReanimatedSensor] set sensor callback failed";
164
+ return false;
165
+ }
166
+ Sensor_Result sensorRes = OH_Sensor_Subscribe(id_, attr, subSensor_);
167
+ if (sensorRes != SENSOR_SUCCESS) {
168
+ LOG(ERROR) << "[ReanimatedSensor] on sensor subscribe failed";
169
+ return false;
170
+ }
171
+ return true;
172
+ }
173
+
174
+
175
+ bool rnoh::ReanimatedSensor::isSupportSensor() {
176
+ uint32_t count = 0;
177
+ int32_t ret = OH_Sensor_GetInfos(nullptr, &count);
178
+ bool isSupport = false;
179
+ if (count > 0) {
180
+ Sensor_Info **sensors = OH_Sensor_CreateInfos(count);
181
+ ret = OH_Sensor_GetInfos(sensors, &count);
182
+ for (uint32_t i = 0; i < count; ++i) {
183
+ Sensor_Info *sensorInfoTemp = sensors[i];
184
+ Sensor_Type sensorType;
185
+ ret = OH_SensorInfo_GetType(sensorInfoTemp, &sensorType);
186
+ if (sensorType == sensorType_) {
187
+ isSupport = true;
188
+ break;
189
+ }
190
+ }
191
+ ret = OH_Sensor_DestroyInfos(sensors, count);
192
+ }
193
+ return isSupport;
194
+ }
195
+
196
+ void rnoh::ReanimatedSensor::runSensor(double *value, size_t count) {
197
+ if (value && setter_ && runOnTheMainThread_ && count > 0) {
198
+ // 跨线程传递必须拷贝到堆:传感器回调运行在传感器线程,
199
+ // 而 setter_ 在主线程异步执行,栈上数据此时已释放(use-after-free)。
200
+ auto dataPtr = std::make_shared<std::vector<double>>(value, value + count);
201
+ runOnTheMainThread_([weakSelf = weak_from_this(), dataPtr]() {
202
+ auto self = weakSelf.lock();
203
+ if (!self) {
204
+ return;
205
+ }
206
+ self->setter_(dataPtr->data(), self->getInterfaceOrientation());
207
+ });
208
+ }
209
+ }
210
+
211
+ int rnoh::ReanimatedSensor::getInterfaceOrientation() {
212
+ NativeDisplayManager_Rotation rotation;
213
+ auto ret = OH_NativeDisplayManager_GetDefaultDisplayRotation(&rotation);
214
+ if (ret == DISPLAY_MANAGER_OK) {
215
+ switch (rotation) {
216
+ case DISPLAY_MANAGER_ROTATION_270:
217
+ return 270;
218
+ case DISPLAY_MANAGER_ROTATION_90:
219
+ return 90;
220
+ case DISPLAY_MANAGER_ROTATION_180:
221
+ return 180;
222
+ default:
223
+ return 0;
224
+ }
225
+ }
226
+ return 0;
227
+ }
228
+
229
+
230
+ void rnoh::ReanimatedSensor::cancel() { destroySensor(); }
231
+
232
+ void rnoh::ReanimatedSensor::destroySensor() {
233
+ std::lock_guard<std::mutex> lock(g_subscriberMapMutex_);
234
+ for (auto it = g_sensorSubscriberMap_.begin(); it != g_sensorSubscriberMap_.end();) {
235
+ if (it->second == this) {
236
+ OH_Sensor_Unsubscribe(it->second->id_, it->second->subSensor_);
237
+ OH_Sensor_DestroySubscriptionId(it->second->id_);
238
+ OH_Sensor_DestroySubscriber(it->second->subSensor_);
239
+ it->second->subSensor_ = nullptr;
240
+ it = g_sensorSubscriberMap_.erase(it);
241
+ } else {
242
+ ++it;
243
+ }
244
+ }
245
+ }
246
+
247
+ rnoh::ReanimatedSensor::~ReanimatedSensor() { destroySensor(); }
248
+ }
@@ -0,0 +1,36 @@
1
+ // Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved
2
+ // Use of this source code is governed by a MIT license that can be
3
+ // found in the LICENSE file.
4
+
5
+ #ifndef HARMONY_REANIMATEDSENSOR_H
6
+ #define HARMONY_REANIMATEDSENSOR_H
7
+ #include <functional>
8
+ #include <mutex>
9
+ #include <sensors/oh_sensor_type.h>
10
+ #include "RNOH/TaskExecutor/TaskExecutor.h"
11
+
12
+ namespace rnoh {
13
+ class ReanimatedSensor : public std::enable_shared_from_this<ReanimatedSensor> {
14
+ public:
15
+ ReanimatedSensor(int sensorType, int interval, int iosReferenceFrame, std::function<void(double[], int)> setter,
16
+ std::function<void(TaskExecutor::Task &&)> runOnTheMainThread);
17
+ ~ReanimatedSensor();
18
+ bool initialize();
19
+ void cancel();
20
+ void castSensorType(int sensorType);
21
+ void runSensor(double *value, size_t count);
22
+ std::function<void(double[], int)> setter_;
23
+ Sensor_Type sensorType_{SENSOR_TYPE_ACCELEROMETER};
24
+
25
+ private:
26
+ void destroySensor();
27
+ bool isSupportSensor();
28
+ int getInterfaceOrientation();
29
+ int interval_{-1};
30
+ int iosReferenceFrame_{-1};
31
+ Sensor_Subscriber *subSensor_{nullptr};
32
+ Sensor_SubscriptionId *id_{nullptr};
33
+ std::function<void(TaskExecutor::Task &&)> runOnTheMainThread_;
34
+ };
35
+ } // namespace rnoh
36
+ #endif // HARMONY_REANIMATEDSENSOR_H
@@ -0,0 +1,36 @@
1
+ // Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved
2
+ // Use of this source code is governed by a MIT license that can be
3
+ // found in the LICENSE file.
4
+
5
+ #include "ReanimatedSensorContainer.h"
6
+ #include "glog/logging.h"
7
+ namespace rnoh {
8
+
9
+ rnoh::ReanimatedSensorContainer::ReanimatedSensorContainer() {}
10
+
11
+ int rnoh::ReanimatedSensorContainer::registerSensor(int sensorType, int interval, int iosReferenceFrame,
12
+ std::function<void(double *, int)> setter,
13
+ std::function<void(TaskExecutor::Task &&)> runOnTheMainThread) {
14
+ auto sensor =
15
+ std::make_shared<ReanimatedSensor>(sensorType, interval, iosReferenceFrame, setter, runOnTheMainThread);
16
+ int sensorId = -1;
17
+ if (sensor->initialize()) {
18
+ sensorId = nextSensorId;
19
+ nextSensorId++;
20
+ sensorMap_[sensorId] = sensor;
21
+ }
22
+ return sensorId;
23
+ }
24
+
25
+ void rnoh::ReanimatedSensorContainer::unregisterSensor(int sensorId) {
26
+ auto it = sensorMap_.find(sensorId);
27
+ if (it == sensorMap_.end()) {
28
+ return;
29
+ }
30
+ auto sensor = it->second;
31
+ if (sensor != nullptr) {
32
+ sensor->cancel();
33
+ sensorMap_.erase(it);
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,23 @@
1
+ // Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved
2
+ // Use of this source code is governed by a MIT license that can be
3
+ // found in the LICENSE file.
4
+
5
+ #ifndef HARMONY_REANIMATEDSENSORCONTAINER_H
6
+ #define HARMONY_REANIMATEDSENSORCONTAINER_H
7
+ #include "ReanimatedSensor.h"
8
+ #include <functional>
9
+ namespace rnoh {
10
+ class ReanimatedSensorContainer {
11
+ public:
12
+ ReanimatedSensorContainer();
13
+ ~ReanimatedSensorContainer() noexcept = default;
14
+ int registerSensor(int sensorType, int interval, int iosReferenceFrame, std::function<void(double[], int)> setter,
15
+ std::function<void(TaskExecutor::Task &&)> runOnTheMainThread);
16
+ void unregisterSensor(int sensorId);
17
+
18
+ private:
19
+ std::unordered_map<int, std::shared_ptr<ReanimatedSensor>> sensorMap_;
20
+ int nextSensorId{0};
21
+ };
22
+ } // namespace rnoh
23
+ #endif // HARMONY_REANIMATEDSENSORCONTAINER_H
@@ -27,6 +27,7 @@
27
27
  #endif // RCT_NEW_ARCH_ENABLED
28
28
 
29
29
  #include <functional>
30
+ #include <glog/logging.h>
30
31
  #include <utility>
31
32
 
32
33
  #ifdef RCT_NEW_ARCH_ENABLED
@@ -228,6 +229,14 @@ void ReanimatedModuleProxy::init(
228
229
  }
229
230
 
230
231
  ReanimatedModuleProxy::~ReanimatedModuleProxy() {
232
+ LOG(WARNING) << "[RNOH_REA_TEARDOWN_V1] stage=proxy_dtor"
233
+ << " frame_callbacks=" << frameCallbacks_.size()
234
+ #ifdef RCT_NEW_ARCH_ENABLED
235
+ << " pending_operations=" << operationsInBatch_.size()
236
+ << " props_registry=" << static_cast<bool>(propsRegistry_)
237
+ << " layout_proxy=" << static_cast<bool>(layoutAnimationsProxy_)
238
+ #endif
239
+ ;
231
240
  // event handler registry and frame callbacks store some JSI values from UI
232
241
  // runtime, so they have to go away before we tear down the runtime
233
242
  eventHandlerRegistry_.reset();
@@ -256,8 +265,42 @@ ReanimatedModuleProxy::~ReanimatedModuleProxy() {
256
265
  }
257
266
  layoutAnimationsProxy_.reset();
258
267
 
268
+ // 主动触发 Hermes GC:在 uiWorkletRuntime_ 销毁前回收 Hermes 堆中持有
269
+ // ShadowNode::Shared(通过 NativeState / ShadowNodeWrapper)的 JSI 对象。
270
+ //
271
+ // 此处 commitHook_ / mountHook_ 仍未 reset,它们持有 uiManager_ 的
272
+ // shared_ptr,确保 GC 回收 ShadowNodeWrapper 触发 ~ShadowNodeFamily 时
273
+ // ComponentDescriptorRegistry 仍然有效。这样可避免 uiWorkletRuntime_.reset()
274
+ // → HadesGC::finalizeAll() 在上下文可能已失效后触发
275
+ // ~ShadowNodeFamily → ~InstanceHandle 崩溃(0730 复现变体根因)。
276
+ //
277
+ // UI runtime 为 LockableRuntime(supportsLocking=true),每个 JSI 操作
278
+ // 自动加锁 runtimeMutex_(recursive_mutex),从主线程调用 gc() 会阻塞
279
+ // 等待 UI 线程完成当前 worklet 后执行,不会死锁。
280
+ #if JS_RUNTIME_HERMES
281
+ if (uiWorkletRuntime_) {
282
+ try {
283
+ auto &uiRuntime = uiWorkletRuntime_->getJSIRuntime();
284
+ auto gc = uiRuntime.global().getPropertyAsFunction(uiRuntime, "gc");
285
+ gc.call(uiRuntime);
286
+ } catch (const std::exception &) {
287
+ // gc() 调用失败不应阻止后续析构流程
288
+ } catch (...) {
289
+ // 忽略未知异常,保证析构不因 GC 失败而 terminate
290
+ }
291
+ }
292
+ #endif // JS_RUNTIME_HERMES
293
+
294
+ // GC 之后再注销 hooks:确保 GC 回收 ShadowNodeWrapper 时
295
+ // UIManager(及 ComponentDescriptorRegistry)仍存活
296
+ commitHook_.reset();
297
+ mountHook_.reset();
298
+ LOG(WARNING) << "[RNOH_REA_TEARDOWN_V1] stage=fabric_hooks_removed";
299
+ uiManager_.reset();
259
300
  #endif // RCT_NEW_ARCH_ENABLED
301
+ LOG(WARNING) << "[RNOH_REA_TEARDOWN_V1] stage=ui_runtime_reset_begin";
260
302
  uiWorkletRuntime_.reset();
303
+ LOG(WARNING) << "[RNOH_REA_TEARDOWN_V1] stage=proxy_dtor_complete";
261
304
  }
262
305
 
263
306
  void ReanimatedModuleProxy::scheduleOnUI(
@@ -622,6 +665,7 @@ void ReanimatedModuleProxy::cleanupSensors() {
622
665
  void ReanimatedModuleProxy::markNodeAsRemovable(
623
666
  jsi::Runtime &rt,
624
667
  const jsi::Value &shadowNodeWrapper) {
668
+ auto lock = propsRegistry_->createLock();
625
669
  auto shadowNode = shadowNodeFromValue(rt, shadowNodeWrapper);
626
670
  propsRegistry_->markNodeAsRemovable(shadowNode);
627
671
  }
@@ -629,6 +673,7 @@ void ReanimatedModuleProxy::markNodeAsRemovable(
629
673
  void ReanimatedModuleProxy::unmarkNodeAsRemovable(
630
674
  jsi::Runtime &rt,
631
675
  const jsi::Value &viewTag) {
676
+ auto lock = propsRegistry_->createLock();
632
677
  propsRegistry_->unmarkNodeAsRemovable(viewTag.asNumber());
633
678
  }
634
679
 
@@ -5,6 +5,8 @@
5
5
  import { RNOHLogger, Tag, UITurboModule, UITurboModuleContext } from "@rnoh/react-native-openharmony/ts";
6
6
  import { Animator, AnimatorResult, window } from '@kit.ArkUI';
7
7
  import { KeyboardState } from "./AnimationManager";
8
+ import deviceInfo from '@ohos.deviceInfo';
9
+ import { accessibility } from '@kit.AccessibilityKit';
8
10
 
9
11
  export class ReanimatedModule extends UITurboModule {
10
12
  static NAME = "ReanimatedModule"
@@ -24,7 +26,13 @@ export class ReanimatedModule extends UITurboModule {
24
26
 
25
27
  public async subscribeKeyBordListeners(isStatusBarTranslucent: boolean, isNavigationBarTranslucent: boolean) {
26
28
  //ignore isStatusBarTranslucent - those are Android only
27
- const windowInstance = await window.getLastWindow(this.ctx.uiAbilityContext);
29
+ let windowInstance: window.Window;
30
+ try {
31
+ windowInstance = await window.getLastWindow(this.ctx.uiAbilityContext);
32
+ } catch (e: ESObject) {
33
+ this.logger.error(`subscribeKeyBordListeners failed: ${e?.message ?? e}`);
34
+ return;
35
+ }
28
36
  const navIndicatorArea = windowInstance.getWindowAvoidArea(
29
37
  window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR
30
38
  );
@@ -91,7 +99,13 @@ export class ReanimatedModule extends UITurboModule {
91
99
  }
92
100
 
93
101
  public async unsubscribeKeyBordListeners() {
94
- const windowInstance = await window.getLastWindow(this.ctx.uiAbilityContext);
102
+ let windowInstance: window.Window;
103
+ try {
104
+ windowInstance = await window.getLastWindow(this.ctx.uiAbilityContext);
105
+ } catch (e: ESObject) {
106
+ this.logger.error(`unsubscribeKeyBordListeners failed: ${e?.message ?? e}`);
107
+ return;
108
+ }
95
109
  windowInstance.off('keyboardHeightChange')
96
110
  if (this.currentAnimator) {
97
111
  this.currentAnimator.onFrame = null;
@@ -120,4 +134,15 @@ export class ReanimatedModule extends UITurboModule {
120
134
  }
121
135
  return isLayoutFullScreen;
122
136
  }
137
+
138
+ public isReducedMotionEnabled(): boolean {
139
+ try {
140
+ if (deviceInfo.sdkApiVersion >= 23) {
141
+ return (accessibility as ESObject).isAnimationReduceEnabledSync();
142
+ }
143
+ } catch (e) {
144
+ this.logger.warn(`isReducedMotionEnabled failed: ${JSON.stringify(e)}`);
145
+ }
146
+ return false;
147
+ }
123
148
  }
Binary file
package/package.json CHANGED
@@ -4,8 +4,8 @@
4
4
  "alias": "react-native-reanimated",
5
5
  "redirectInternalImports": true
6
6
  },
7
- "version": "3.18.3-beta.2",
8
- "description": "",
7
+ "version": "3.18.4-beta.2",
8
+ "description": "More powerful alternative to Animated library for React Native.",
9
9
  "main": "lib/index.js",
10
10
  "scripts": {
11
11
  "build": "tsc",
@@ -46,6 +46,20 @@
46
46
  "lib",
47
47
  "harmony",
48
48
  "src",
49
- "./harmony.tar.gz"
50
- ]
49
+ "./harmony.tar.gz",
50
+ "CHANGELOG.md",
51
+ "!build"
52
+ ],
53
+ "publishConfig": {
54
+ "access": "public",
55
+ "registry": "https://registry.npmjs.org/"
56
+ },
57
+ "homepage": "https://gitcode.com/CPF-RN/rntpc_react-native-reanimated",
58
+ "bugs": {
59
+ "url": "https://gitcode.com/CPF-RN/rntpc_react-native-reanimated/issues"
60
+ },
61
+ "repository": {
62
+ "type": "git",
63
+ "url": "https://gitcode.com/CPF-RN/rntpc_react-native-reanimated.git"
64
+ }
51
65
  }