@onekeyfe/react-native-perf-stats 3.0.25

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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +64 -0
  3. package/ReactNativePerfStats.podspec +30 -0
  4. package/android/CMakeLists.txt +24 -0
  5. package/android/build.gradle +130 -0
  6. package/android/gradle.properties +4 -0
  7. package/android/src/main/AndroidManifest.xml +8 -0
  8. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  9. package/android/src/main/java/com/margelo/nitro/reactnativeperfstats/PerfStatsInitProvider.kt +43 -0
  10. package/android/src/main/java/com/margelo/nitro/reactnativeperfstats/ReactNativePerfStats.kt +514 -0
  11. package/android/src/main/java/com/margelo/nitro/reactnativeperfstats/ReactNativePerfStatsPackage.kt +24 -0
  12. package/ios/ReactNativePerfStats.swift +391 -0
  13. package/lib/module/ReactNativePerfStats.nitro.js +4 -0
  14. package/lib/module/ReactNativePerfStats.nitro.js.map +1 -0
  15. package/lib/module/index.js +6 -0
  16. package/lib/module/index.js.map +1 -0
  17. package/lib/module/package.json +1 -0
  18. package/lib/typescript/package.json +1 -0
  19. package/lib/typescript/src/ReactNativePerfStats.nitro.d.ts +51 -0
  20. package/lib/typescript/src/ReactNativePerfStats.nitro.d.ts.map +1 -0
  21. package/lib/typescript/src/index.d.ts +4 -0
  22. package/lib/typescript/src/index.d.ts.map +1 -0
  23. package/nitro.json +17 -0
  24. package/nitrogen/generated/android/c++/JHybridReactNativePerfStatsSpec.cpp +83 -0
  25. package/nitrogen/generated/android/c++/JHybridReactNativePerfStatsSpec.hpp +69 -0
  26. package/nitrogen/generated/android/c++/JPerfSample.hpp +65 -0
  27. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativeperfstats/HybridReactNativePerfStatsSpec.kt +74 -0
  28. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativeperfstats/PerfSample.kt +44 -0
  29. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativeperfstats/reactnativeperfstatsOnLoad.kt +35 -0
  30. package/nitrogen/generated/android/reactnativeperfstats+autolinking.cmake +81 -0
  31. package/nitrogen/generated/android/reactnativeperfstats+autolinking.gradle +27 -0
  32. package/nitrogen/generated/android/reactnativeperfstatsOnLoad.cpp +44 -0
  33. package/nitrogen/generated/android/reactnativeperfstatsOnLoad.hpp +25 -0
  34. package/nitrogen/generated/ios/ReactNativePerfStats+autolinking.rb +60 -0
  35. package/nitrogen/generated/ios/ReactNativePerfStats-Swift-Cxx-Bridge.cpp +49 -0
  36. package/nitrogen/generated/ios/ReactNativePerfStats-Swift-Cxx-Bridge.hpp +122 -0
  37. package/nitrogen/generated/ios/ReactNativePerfStats-Swift-Cxx-Umbrella.hpp +47 -0
  38. package/nitrogen/generated/ios/ReactNativePerfStatsAutolinking.mm +33 -0
  39. package/nitrogen/generated/ios/ReactNativePerfStatsAutolinking.swift +25 -0
  40. package/nitrogen/generated/ios/c++/HybridReactNativePerfStatsSpecSwift.cpp +11 -0
  41. package/nitrogen/generated/ios/c++/HybridReactNativePerfStatsSpecSwift.hpp +102 -0
  42. package/nitrogen/generated/ios/swift/Func_void_PerfSample.swift +47 -0
  43. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
  44. package/nitrogen/generated/ios/swift/HybridReactNativePerfStatsSpec.swift +60 -0
  45. package/nitrogen/generated/ios/swift/HybridReactNativePerfStatsSpec_cxx.swift +182 -0
  46. package/nitrogen/generated/ios/swift/PerfSample.swift +58 -0
  47. package/nitrogen/generated/shared/c++/HybridReactNativePerfStatsSpec.cpp +25 -0
  48. package/nitrogen/generated/shared/c++/HybridReactNativePerfStatsSpec.hpp +68 -0
  49. package/nitrogen/generated/shared/c++/PerfSample.hpp +83 -0
  50. package/package.json +169 -0
  51. package/src/ReactNativePerfStats.nitro.ts +54 -0
  52. package/src/index.tsx +8 -0
@@ -0,0 +1,391 @@
1
+ import NitroModules
2
+ import ReactNativeNativeLogger
3
+ import Darwin
4
+ import UIKit
5
+
6
+ private let kTag = "PerfStats"
7
+ private let kMinIntervalMs: Double = 200
8
+
9
+ // Anomaly logging thresholds. We only emit a warn after the metric has
10
+ // stayed over the threshold for kAnomalySustainSamples in a row, to
11
+ // skip transient spikes (e.g. JS startup, GC). After firing we throttle
12
+ // for kAnomalyCooldownSec to avoid flooding native-logger.
13
+ private let kCpuAnomalyPct: Double = 150.0
14
+ private let kRssAnomalyBytes: Double = 800.0 * 1024.0 * 1024.0
15
+ private let kAnomalySustainSamples: Int = 5
16
+ private let kAnomalyCooldownSec: Double = 30.0
17
+
18
+ class ReactNativePerfStats: HybridReactNativePerfStatsSpec {
19
+
20
+ func start(intervalMs: Double) throws {
21
+ Sampler.shared.start(intervalMs: max(intervalMs, kMinIntervalMs))
22
+ }
23
+
24
+ func stop() throws {
25
+ Sampler.shared.stop()
26
+ }
27
+
28
+ func showOverlay() throws {
29
+ Overlay.shared.show()
30
+ }
31
+
32
+ func hideOverlay() throws {
33
+ Overlay.shared.hide()
34
+ }
35
+
36
+ func sample() throws -> Promise<PerfSample> {
37
+ return Promise.async {
38
+ return Sampler.shared.takeSample()
39
+ }
40
+ }
41
+ }
42
+
43
+ // MARK: - Sampler
44
+ //
45
+ // Singleton; one timer fires on a private background queue regardless of
46
+ // how many HybridObject instances exist or which thread calls start(). This
47
+ // keeps overlay updates flowing even when the JS thread is blocked.
48
+
49
+ private final class Sampler {
50
+ static let shared = Sampler()
51
+
52
+ private let queue = DispatchQueue(label: "io.onekey.perfstats.sampler", qos: .utility)
53
+ private let lock = NSLock()
54
+ private var timer: DispatchSourceTimer?
55
+ private var running = false
56
+ private var intervalMs: Double = 1000
57
+ private var lastCpuSec: Double = -1
58
+ private var lastMonoSec: Double = -1
59
+
60
+ // Anomaly state lives on the sampler's serial dispatch queue, so no
61
+ // synchronization is needed. lastLogSec intentionally persists across
62
+ // stop()/start() cycles to keep the cooldown honest if the caller
63
+ // toggles the sampler rapidly.
64
+ private var cpuOverCount: Int = 0
65
+ private var rssOverCount: Int = 0
66
+ private var lastCpuLogSec: Double = 0
67
+ private var lastRssLogSec: Double = 0
68
+
69
+ func start(intervalMs ms: Double) {
70
+ queue.async { [weak self] in
71
+ guard let self = self else { return }
72
+ let prev = self.intervalMs
73
+ self.intervalMs = ms
74
+ if self.running, let t = self.timer {
75
+ // Skip reschedule when interval is unchanged — calling
76
+ // `schedule` on a running source resets the next deadline,
77
+ // which would briefly extend the gap unnecessarily.
78
+ if prev != ms {
79
+ t.schedule(
80
+ deadline: .now() + .milliseconds(Int(ms)),
81
+ repeating: .milliseconds(Int(ms))
82
+ )
83
+ }
84
+ return
85
+ }
86
+ self.running = true
87
+ let t = DispatchSource.makeTimerSource(queue: self.queue)
88
+ t.schedule(
89
+ deadline: .now() + .milliseconds(Int(ms)),
90
+ repeating: .milliseconds(Int(ms))
91
+ )
92
+ t.setEventHandler { [weak self] in
93
+ guard let self = self, self.running else { return }
94
+ let s = self.takeSample()
95
+ Overlay.shared.update(sample: s)
96
+ self.checkAnomalyAndLog(sample: s)
97
+ }
98
+ self.timer = t
99
+ t.resume()
100
+ }
101
+ }
102
+
103
+ func stop() {
104
+ queue.async { [weak self] in
105
+ guard let self = self else { return }
106
+ self.running = false
107
+ self.timer?.cancel()
108
+ self.timer = nil
109
+ }
110
+ Overlay.shared.hide()
111
+ }
112
+
113
+ func takeSample() -> PerfSample {
114
+ let nowMono = monotonicSec()
115
+ let nowCpu = processCpuSec()
116
+ let rssBytes = residentBytes()
117
+ let nowWallMs = Date().timeIntervalSince1970 * 1000.0
118
+
119
+ var cpuPct: Double = 0
120
+ lock.lock()
121
+ if nowCpu >= 0 && lastCpuSec >= 0 && lastMonoSec > 0 {
122
+ let dCpu = nowCpu - lastCpuSec
123
+ let dWall = nowMono - lastMonoSec
124
+ if dWall > 0 && dCpu >= 0 {
125
+ cpuPct = (dCpu / dWall) * 100.0
126
+ }
127
+ }
128
+ if nowCpu >= 0 {
129
+ lastCpuSec = nowCpu
130
+ lastMonoSec = nowMono
131
+ }
132
+ lock.unlock()
133
+
134
+ return PerfSample(
135
+ cpu: cpuPct,
136
+ rss: Double(rssBytes),
137
+ timestamp: nowWallMs
138
+ )
139
+ }
140
+
141
+ /// Emits a warn to native-logger when CPU or RSS has stayed over its
142
+ /// threshold for `kAnomalySustainSamples` consecutive samples. Only
143
+ /// called from the periodic timer; one-off `sample()` calls do not
144
+ /// trip this path. Each metric tracks its own counter and cooldown.
145
+ private func checkAnomalyAndLog(sample s: PerfSample) {
146
+ let nowSec = monotonicSec()
147
+
148
+ if s.cpu >= kCpuAnomalyPct {
149
+ cpuOverCount += 1
150
+ if cpuOverCount >= kAnomalySustainSamples,
151
+ nowSec - lastCpuLogSec >= kAnomalyCooldownSec {
152
+ OneKeyLog.warn(
153
+ kTag,
154
+ String(
155
+ format: "Sustained high CPU: %.1f%% over %d samples",
156
+ s.cpu, cpuOverCount
157
+ )
158
+ )
159
+ lastCpuLogSec = nowSec
160
+ cpuOverCount = 0
161
+ }
162
+ } else {
163
+ cpuOverCount = 0
164
+ }
165
+
166
+ if s.rss >= kRssAnomalyBytes {
167
+ rssOverCount += 1
168
+ if rssOverCount >= kAnomalySustainSamples,
169
+ nowSec - lastRssLogSec >= kAnomalyCooldownSec {
170
+ let mb = s.rss / 1024.0 / 1024.0
171
+ OneKeyLog.warn(
172
+ kTag,
173
+ String(
174
+ format: "Sustained high RSS: %.1f MB over %d samples",
175
+ mb, rssOverCount
176
+ )
177
+ )
178
+ lastRssLogSec = nowSec
179
+ rssOverCount = 0
180
+ }
181
+ } else {
182
+ rssOverCount = 0
183
+ }
184
+ }
185
+
186
+ private func monotonicSec() -> Double {
187
+ var ts = timespec()
188
+ if clock_gettime(CLOCK_MONOTONIC, &ts) != 0 {
189
+ return Date().timeIntervalSince1970
190
+ }
191
+ return Double(ts.tv_sec) + Double(ts.tv_nsec) / 1_000_000_000.0
192
+ }
193
+
194
+ /// Aggregate CPU time consumed by all threads (live + terminated) of the
195
+ /// current process, in seconds. Returns -1 on failure.
196
+ private func processCpuSec() -> Double {
197
+ var ts = timespec()
198
+ if clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) != 0 {
199
+ OneKeyLog.warn(kTag, "clock_gettime(CLOCK_PROCESS_CPUTIME_ID) failed: errno=\(errno)")
200
+ return -1
201
+ }
202
+ return Double(ts.tv_sec) + Double(ts.tv_nsec) / 1_000_000_000.0
203
+ }
204
+
205
+ private func residentBytes() -> UInt64 {
206
+ var vmInfo = task_vm_info_data_t()
207
+ var count = mach_msg_type_number_t(
208
+ MemoryLayout<task_vm_info_data_t>.size / MemoryLayout<natural_t>.size
209
+ )
210
+ let kr = withUnsafeMutablePointer(to: &vmInfo) { ptr in
211
+ ptr.withMemoryRebound(to: integer_t.self, capacity: Int(count)) { intPtr in
212
+ task_info(mach_task_self_, task_flavor_t(TASK_VM_INFO), intPtr, &count)
213
+ }
214
+ }
215
+ if kr == KERN_SUCCESS {
216
+ return UInt64(vmInfo.phys_footprint)
217
+ }
218
+
219
+ var basicInfo = mach_task_basic_info_data_t()
220
+ var basicCount = mach_msg_type_number_t(
221
+ MemoryLayout<mach_task_basic_info_data_t>.size / MemoryLayout<natural_t>.size
222
+ )
223
+ let basicKr = withUnsafeMutablePointer(to: &basicInfo) { ptr in
224
+ ptr.withMemoryRebound(to: integer_t.self, capacity: Int(basicCount)) { intPtr in
225
+ task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), intPtr, &basicCount)
226
+ }
227
+ }
228
+ if basicKr == KERN_SUCCESS {
229
+ return UInt64(basicInfo.resident_size)
230
+ }
231
+
232
+ OneKeyLog.warn(kTag, "Failed to read resident memory; vmKr=\(kr) basicKr=\(basicKr)")
233
+ return 0
234
+ }
235
+ }
236
+
237
+ // MARK: - Overlay
238
+ //
239
+ // Singleton UILabel attached to the current key UIWindow. Updates always
240
+ // dispatch to main. No floating-window permission needed; overlay only
241
+ // shows while the app is in the foreground.
242
+ //
243
+ // Inherits NSObject so UIPanGestureRecognizer's target/action selector
244
+ // dispatch resolves cleanly via Obj-C runtime.
245
+
246
+ private final class Overlay: NSObject {
247
+ static let shared = Overlay()
248
+
249
+ private override init() { super.init() }
250
+
251
+ private var label: UILabel?
252
+ private var visible = false
253
+
254
+ // `_lastSample` is written by the Sampler timer thread and read by the
255
+ // main thread in attach() and inside the coalesced update closure.
256
+ // Optional<struct> is not atomic in Swift, so guard with a lock to avoid
257
+ // torn reads / undefined behaviour. `_updatePending` is part of the same
258
+ // protected state to coalesce overlay refreshes.
259
+ private let sampleLock = NSLock()
260
+ private var _lastSample: PerfSample?
261
+ private var _updatePending = false
262
+
263
+ private var lastSample: PerfSample? {
264
+ get {
265
+ sampleLock.lock(); defer { sampleLock.unlock() }
266
+ return _lastSample
267
+ }
268
+ set {
269
+ sampleLock.lock(); defer { sampleLock.unlock() }
270
+ _lastSample = newValue
271
+ }
272
+ }
273
+
274
+ func show() {
275
+ visible = true
276
+ DispatchQueue.main.async { [weak self] in self?.attach() }
277
+ }
278
+
279
+ func hide() {
280
+ visible = false
281
+ DispatchQueue.main.async { [weak self] in self?.detach() }
282
+ }
283
+
284
+ /// Coalesce updates: if the main thread is blocked, we don't want N
285
+ /// label.text writes piling up only to all fire when it unblocks. The
286
+ /// `_updatePending` flag (held under sampleLock) ensures at most one
287
+ /// pending dispatch at a time, and the closure always reads the
288
+ /// latest sample on main.
289
+ func update(sample: PerfSample) {
290
+ sampleLock.lock()
291
+ _lastSample = sample
292
+ let shouldPost = !_updatePending
293
+ if shouldPost { _updatePending = true }
294
+ sampleLock.unlock()
295
+
296
+ if !shouldPost { return }
297
+
298
+ DispatchQueue.main.async { [weak self] in
299
+ guard let self = self else { return }
300
+ self.sampleLock.lock()
301
+ self._updatePending = false
302
+ let snapshot = self._lastSample
303
+ self.sampleLock.unlock()
304
+ guard self.visible, let snapshot = snapshot else { return }
305
+ self.label?.text = self.renderText(snapshot)
306
+ }
307
+ }
308
+
309
+ private func attach() {
310
+ if label != nil { return }
311
+ guard let window = currentKeyWindow() else {
312
+ OneKeyLog.warn(kTag, "No key UIWindow available; overlay deferred")
313
+ return
314
+ }
315
+
316
+ let lbl = UILabel(frame: CGRect(x: 30, y: 100, width: 160, height: 60))
317
+ lbl.backgroundColor = UIColor.black.withAlphaComponent(0.7)
318
+ lbl.layer.cornerRadius = 8
319
+ lbl.layer.masksToBounds = true
320
+ lbl.textColor = .white
321
+ lbl.font = .systemFont(ofSize: 13, weight: .bold)
322
+ lbl.textAlignment = .center
323
+ lbl.numberOfLines = 2
324
+ lbl.text = "CPU: --\nRAM: --"
325
+ lbl.isUserInteractionEnabled = true
326
+
327
+ let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
328
+ lbl.addGestureRecognizer(pan)
329
+
330
+ window.addSubview(lbl)
331
+ label = lbl
332
+
333
+ if let s = lastSample {
334
+ lbl.text = renderText(s)
335
+ }
336
+ }
337
+
338
+ private func detach() {
339
+ label?.removeFromSuperview()
340
+ label = nil
341
+ }
342
+
343
+ @objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
344
+ guard let view = gesture.view, let parent = view.superview else { return }
345
+ let translation = gesture.translation(in: parent)
346
+ let newCenter = CGPoint(
347
+ x: view.center.x + translation.x,
348
+ y: view.center.y + translation.y
349
+ )
350
+ let halfW = view.bounds.size.width / 2
351
+ let halfH = view.bounds.size.height / 2
352
+ view.center = CGPoint(
353
+ x: max(halfW, min(parent.bounds.size.width - halfW, newCenter.x)),
354
+ y: max(halfH, min(parent.bounds.size.height - halfH, newCenter.y))
355
+ )
356
+ gesture.setTranslation(.zero, in: parent)
357
+ }
358
+
359
+ private func renderText(_ s: PerfSample) -> String {
360
+ let cpuStr = s.cpu > 0 ? String(format: "%.1f%%", s.cpu) : "--"
361
+ let mb = s.rss / 1024.0 / 1024.0
362
+ let memStr = mb > 0 ? String(format: "%.1f MB", mb) : "--"
363
+ return "CPU: \(cpuStr)\nRAM: \(memStr)"
364
+ }
365
+
366
+ private func currentKeyWindow() -> UIWindow? {
367
+ // iOS 15+ preferred path
368
+ if #available(iOS 15.0, *) {
369
+ for scene in UIApplication.shared.connectedScenes {
370
+ guard
371
+ let windowScene = scene as? UIWindowScene,
372
+ windowScene.activationState == .foregroundActive
373
+ else { continue }
374
+ if let kw = windowScene.keyWindow {
375
+ return kw
376
+ }
377
+ }
378
+ }
379
+ // Fallback: scan all connected scenes
380
+ for scene in UIApplication.shared.connectedScenes {
381
+ guard let windowScene = scene as? UIWindowScene else { continue }
382
+ if let kw = windowScene.windows.first(where: { $0.isKeyWindow }) {
383
+ return kw
384
+ }
385
+ if let any = windowScene.windows.first {
386
+ return any
387
+ }
388
+ }
389
+ return nil
390
+ }
391
+ }
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export {};
4
+ //# sourceMappingURL=ReactNativePerfStats.nitro.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"../../src","sources":["ReactNativePerfStats.nitro.ts"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+
3
+ import { NitroModules } from 'react-native-nitro-modules';
4
+ const ReactNativePerfStatsHybridObject = NitroModules.createHybridObject('ReactNativePerfStats');
5
+ export const ReactNativePerfStats = ReactNativePerfStatsHybridObject;
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NitroModules","ReactNativePerfStatsHybridObject","createHybridObject","ReactNativePerfStats"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,4BAA4B;AAGzD,MAAMC,gCAAgC,GACpCD,YAAY,CAACE,kBAAkB,CAA2B,sBAAsB,CAAC;AAEnF,OAAO,MAAMC,oBAAoB,GAAGF,gCAAgC","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,51 @@
1
+ import type { HybridObject } from 'react-native-nitro-modules';
2
+ export interface PerfSample {
3
+ /**
4
+ * Process CPU usage as a percentage of one core, computed as
5
+ * `(deltaCpuTime / deltaWallTime) * 100` against the previous sample.
6
+ * Multi-core saturation can exceed 100. The first sample after process
7
+ * launch returns `0` because no baseline exists yet.
8
+ */
9
+ cpu: number;
10
+ /** Resident set size in bytes. iOS: phys_footprint; Android: VmRSS. */
11
+ rss: number;
12
+ /** Wall-clock timestamp (ms since unix epoch) when the sample was taken. */
13
+ timestamp: number;
14
+ }
15
+ export interface ReactNativePerfStats extends HybridObject<{
16
+ ios: 'swift';
17
+ android: 'kotlin';
18
+ }> {
19
+ /**
20
+ * Start the singleton native sampler.
21
+ *
22
+ * - Runs on a dedicated background thread (Android: HandlerThread;
23
+ * iOS: dispatch source on a global queue), so it survives JS-thread
24
+ * blockages — the overlay keeps updating even when JS is frozen.
25
+ * - Idempotent: calling `start` again only updates the interval; it does
26
+ * not spawn additional timers.
27
+ */
28
+ start(intervalMs: number): void;
29
+ /** Stop the sampler. Also hides the overlay if shown. No-op if not running. */
30
+ stop(): void;
31
+ /**
32
+ * Show the floating overlay (CPU + RAM) drawn natively.
33
+ *
34
+ * - Android: TextView attached to the current Activity via
35
+ * `addContentView` — no floating-window permission required.
36
+ * - iOS: UILabel added to the key UIWindow.
37
+ * - Draggable on both platforms.
38
+ * - Idempotent. If the sampler is not running, the overlay shows "--"
39
+ * until `start` is called.
40
+ */
41
+ showOverlay(): void;
42
+ /** Hide the floating overlay. The sampler keeps running. No-op if not shown. */
43
+ hideOverlay(): void;
44
+ /**
45
+ * Take one CPU+RAM sample without affecting the overlay timer.
46
+ * Cheap (~1ms) and runs off the JS thread via Promise.async.
47
+ * Shares the same delta baseline as the overlay sampler.
48
+ */
49
+ sample(): Promise<PerfSample>;
50
+ }
51
+ //# sourceMappingURL=ReactNativePerfStats.nitro.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReactNativePerfStats.nitro.d.ts","sourceRoot":"","sources":["../../../src/ReactNativePerfStats.nitro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBACf,SAAQ,YAAY,CAAC;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,CAAC;IACzD;;;;;;;;OAQG;IACH,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC,+EAA+E;IAC/E,IAAI,IAAI,IAAI,CAAC;IAEb;;;;;;;;;OASG;IACH,WAAW,IAAI,IAAI,CAAC;IAEpB,gFAAgF;IAChF,WAAW,IAAI,IAAI,CAAC;IAEpB;;;;OAIG;IACH,MAAM,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAC/B"}
@@ -0,0 +1,4 @@
1
+ import type { ReactNativePerfStats as ReactNativePerfStatsType } from './ReactNativePerfStats.nitro';
2
+ export declare const ReactNativePerfStats: ReactNativePerfStatsType;
3
+ export type * from './ReactNativePerfStats.nitro';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,IAAI,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAKrG,eAAO,MAAM,oBAAoB,0BAAmC,CAAC;AACrE,mBAAmB,8BAA8B,CAAC"}
package/nitro.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "cxxNamespace": ["reactnativeperfstats"],
3
+ "ios": {
4
+ "iosModuleName": "ReactNativePerfStats"
5
+ },
6
+ "android": {
7
+ "androidNamespace": ["reactnativeperfstats"],
8
+ "androidCxxLibName": "reactnativeperfstats"
9
+ },
10
+ "autolinking": {
11
+ "ReactNativePerfStats": {
12
+ "swift": "ReactNativePerfStats",
13
+ "kotlin": "ReactNativePerfStats"
14
+ }
15
+ },
16
+ "ignorePaths": ["node_modules"]
17
+ }
@@ -0,0 +1,83 @@
1
+ ///
2
+ /// JHybridReactNativePerfStatsSpec.cpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © 2026 Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ #include "JHybridReactNativePerfStatsSpec.hpp"
9
+
10
+ // Forward declaration of `PerfSample` to properly resolve imports.
11
+ namespace margelo::nitro::reactnativeperfstats { struct PerfSample; }
12
+
13
+ #include "PerfSample.hpp"
14
+ #include <NitroModules/Promise.hpp>
15
+ #include <NitroModules/JPromise.hpp>
16
+ #include "JPerfSample.hpp"
17
+
18
+ namespace margelo::nitro::reactnativeperfstats {
19
+
20
+ jni::local_ref<JHybridReactNativePerfStatsSpec::jhybriddata> JHybridReactNativePerfStatsSpec::initHybrid(jni::alias_ref<jhybridobject> jThis) {
21
+ return makeCxxInstance(jThis);
22
+ }
23
+
24
+ void JHybridReactNativePerfStatsSpec::registerNatives() {
25
+ registerHybrid({
26
+ makeNativeMethod("initHybrid", JHybridReactNativePerfStatsSpec::initHybrid),
27
+ });
28
+ }
29
+
30
+ size_t JHybridReactNativePerfStatsSpec::getExternalMemorySize() noexcept {
31
+ static const auto method = javaClassStatic()->getMethod<jlong()>("getMemorySize");
32
+ return method(_javaPart);
33
+ }
34
+
35
+ void JHybridReactNativePerfStatsSpec::dispose() noexcept {
36
+ static const auto method = javaClassStatic()->getMethod<void()>("dispose");
37
+ method(_javaPart);
38
+ }
39
+
40
+ std::string JHybridReactNativePerfStatsSpec::toString() {
41
+ static const auto method = javaClassStatic()->getMethod<jni::JString()>("toString");
42
+ auto javaString = method(_javaPart);
43
+ return javaString->toStdString();
44
+ }
45
+
46
+ // Properties
47
+
48
+
49
+ // Methods
50
+ void JHybridReactNativePerfStatsSpec::start(double intervalMs) {
51
+ static const auto method = javaClassStatic()->getMethod<void(double /* intervalMs */)>("start");
52
+ method(_javaPart, intervalMs);
53
+ }
54
+ void JHybridReactNativePerfStatsSpec::stop() {
55
+ static const auto method = javaClassStatic()->getMethod<void()>("stop");
56
+ method(_javaPart);
57
+ }
58
+ void JHybridReactNativePerfStatsSpec::showOverlay() {
59
+ static const auto method = javaClassStatic()->getMethod<void()>("showOverlay");
60
+ method(_javaPart);
61
+ }
62
+ void JHybridReactNativePerfStatsSpec::hideOverlay() {
63
+ static const auto method = javaClassStatic()->getMethod<void()>("hideOverlay");
64
+ method(_javaPart);
65
+ }
66
+ std::shared_ptr<Promise<PerfSample>> JHybridReactNativePerfStatsSpec::sample() {
67
+ static const auto method = javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>()>("sample");
68
+ auto __result = method(_javaPart);
69
+ return [&]() {
70
+ auto __promise = Promise<PerfSample>::create();
71
+ __result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& __boxedResult) {
72
+ auto __result = jni::static_ref_cast<JPerfSample>(__boxedResult);
73
+ __promise->resolve(__result->toCpp());
74
+ });
75
+ __result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
76
+ jni::JniException __jniError(__throwable);
77
+ __promise->reject(std::make_exception_ptr(__jniError));
78
+ });
79
+ return __promise;
80
+ }();
81
+ }
82
+
83
+ } // namespace margelo::nitro::reactnativeperfstats
@@ -0,0 +1,69 @@
1
+ ///
2
+ /// HybridReactNativePerfStatsSpec.hpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © 2026 Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ #pragma once
9
+
10
+ #include <NitroModules/JHybridObject.hpp>
11
+ #include <fbjni/fbjni.h>
12
+ #include "HybridReactNativePerfStatsSpec.hpp"
13
+
14
+
15
+
16
+
17
+ namespace margelo::nitro::reactnativeperfstats {
18
+
19
+ using namespace facebook;
20
+
21
+ class JHybridReactNativePerfStatsSpec: public jni::HybridClass<JHybridReactNativePerfStatsSpec, JHybridObject>,
22
+ public virtual HybridReactNativePerfStatsSpec {
23
+ public:
24
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/reactnativeperfstats/HybridReactNativePerfStatsSpec;";
25
+ static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
26
+ static void registerNatives();
27
+
28
+ protected:
29
+ // C++ constructor (called from Java via `initHybrid()`)
30
+ explicit JHybridReactNativePerfStatsSpec(jni::alias_ref<jhybridobject> jThis) :
31
+ HybridObject(HybridReactNativePerfStatsSpec::TAG),
32
+ HybridBase(jThis),
33
+ _javaPart(jni::make_global(jThis)) {}
34
+
35
+ public:
36
+ ~JHybridReactNativePerfStatsSpec() override {
37
+ // Hermes GC can destroy JS objects on a non-JNI Thread.
38
+ jni::ThreadScope::WithClassLoader([&] { _javaPart.reset(); });
39
+ }
40
+
41
+ public:
42
+ size_t getExternalMemorySize() noexcept override;
43
+ void dispose() noexcept override;
44
+ std::string toString() override;
45
+
46
+ public:
47
+ inline const jni::global_ref<JHybridReactNativePerfStatsSpec::javaobject>& getJavaPart() const noexcept {
48
+ return _javaPart;
49
+ }
50
+
51
+ public:
52
+ // Properties
53
+
54
+
55
+ public:
56
+ // Methods
57
+ void start(double intervalMs) override;
58
+ void stop() override;
59
+ void showOverlay() override;
60
+ void hideOverlay() override;
61
+ std::shared_ptr<Promise<PerfSample>> sample() override;
62
+
63
+ private:
64
+ friend HybridBase;
65
+ using HybridBase::HybridBase;
66
+ jni::global_ref<JHybridReactNativePerfStatsSpec::javaobject> _javaPart;
67
+ };
68
+
69
+ } // namespace margelo::nitro::reactnativeperfstats