@goliapkg/sentori-react-native 6.1.0 → 6.2.1

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 (46) hide show
  1. package/SentoriReactNative.podspec +11 -4
  2. package/android/build.gradle +15 -1
  3. package/android/src/main/java-core/com/sentori/Sentori.kt +244 -0
  4. package/android/src/main/{java → java-core}/com/sentori/SentoriAnrWatchdog.kt +3 -0
  5. package/android/src/main/java-core/com/sentori/SentoriConfig.kt +61 -0
  6. package/android/src/main/{java → java-core}/com/sentori/SentoriCrashHandler.kt +3 -0
  7. package/android/src/main/java-core/com/sentori/SentoriDevice.kt +61 -0
  8. package/android/src/main/{java → java-core}/com/sentori/SentoriFirebaseMessagingService.kt +3 -0
  9. package/android/src/main/{java → java-core}/com/sentori/SentoriForegroundActivity.kt +3 -0
  10. package/android/src/main/java-core/com/sentori/SentoriIdentity.kt +101 -0
  11. package/android/src/main/{java → java-core}/com/sentori/SentoriMobileVitals.kt +3 -0
  12. package/android/src/main/{java → java-core}/com/sentori/SentoriNativeExceptionBridge.kt +3 -0
  13. package/android/src/main/{java → java-core}/com/sentori/SentoriNativeSignals.kt +3 -0
  14. package/android/src/main/java-core/com/sentori/SentoriPush.kt +359 -0
  15. package/android/src/main/{java → java-core}/com/sentori/SentoriPushNotifications.kt +3 -0
  16. package/android/src/main/{java → java-core}/com/sentori/SentoriReplayCapture.kt +3 -0
  17. package/android/src/main/java-core/com/sentori/SentoriScope.kt +60 -0
  18. package/android/src/main/{java → java-core}/com/sentori/SentoriScreenshotCapture.kt +3 -0
  19. package/android/src/main/java-core/com/sentori/SentoriSignalRing.kt +80 -0
  20. package/android/src/main/java-core/com/sentori/SentoriTransport.kt +383 -0
  21. package/android/src/test/{java → java-core}/com/sentori/SentoriCrashHandlerTest.kt +3 -0
  22. package/android/src/test/java-core/com/sentori/SentoriIdentityTest.kt +107 -0
  23. package/android/src/test/java-core/com/sentori/SentoriLiveServerTest.kt +106 -0
  24. package/android/src/test/java-core/com/sentori/SentoriPushTest.kt +151 -0
  25. package/android/src/test/{java → java-core}/com/sentori/SentoriScreenshotCaptureTest.kt +3 -0
  26. package/android/src/test/java-core/com/sentori/SentoriVerbsTest.kt +185 -0
  27. package/ios/{SentoriCrashHandler.swift → core/SentoriCrashHandler.swift} +16 -0
  28. package/ios/{SentoriHangWatchdog.swift → core/SentoriHangWatchdog.swift} +3 -0
  29. package/ios/{SentoriMobileVitals.swift → core/SentoriMobileVitals.swift} +3 -0
  30. package/ios/{SentoriNativeExceptionBridge.swift → core/SentoriNativeExceptionBridge.swift} +3 -0
  31. package/ios/core/SentoriPush.swift +340 -0
  32. package/ios/{SentoriPushNotifications.swift → core/SentoriPushNotifications.swift} +42 -0
  33. package/ios/{SentoriReplayCapture.swift → core/SentoriReplayCapture.swift} +3 -0
  34. package/ios/{SentoriScreenshotCapture.swift → core/SentoriScreenshotCapture.swift} +14 -0
  35. package/ios/{SentoriThreadSampler.swift → core/SentoriThreadSampler.swift} +3 -0
  36. package/lib/push.d.ts +7 -6
  37. package/lib/push.d.ts.map +1 -1
  38. package/lib/push.js +8 -0
  39. package/lib/push.js.map +1 -1
  40. package/lib/transport.js +1 -1
  41. package/package.json +1 -1
  42. package/src/push.ts +14 -6
  43. package/src/transport.ts +1 -1
  44. package/ios/Tests/SentoriCrashHandlerTests.swift +0 -59
  45. package/ios/Tests/SentoriScreenshotCaptureTests.swift +0 -59
  46. package/ios/Tests/SentoriThreadSamplerTests.swift +0 -96
@@ -1,59 +0,0 @@
1
- // Phase 16 sub-E: XCTest coverage for SentoriCrashHandler (回填 Phase 7).
2
- //
3
- // Run via Xcode (target → SentoriTests, ⌘U) or via xcodebuild from the
4
- // iOS host:
5
- // xcodebuild test \
6
- // -scheme SentoriTests \
7
- // -destination 'platform=iOS Simulator,name=iPhone 15'
8
- //
9
- // The handler writes one JSON file per crash to
10
- // <Documents>/sentori/pending/<uuid>.json
11
- // We can't easily re-raise NSException (it tears down the test
12
- // process), so we exercise the persistence helper directly instead.
13
-
14
- import XCTest
15
- @testable import SentoriCrashHandler
16
-
17
- final class SentoriCrashHandlerTests: XCTestCase {
18
- private var pendingDir: URL!
19
-
20
- override func setUpWithError() throws {
21
- let docs = try FileManager.default.url(
22
- for: .documentDirectory,
23
- in: .userDomainMask,
24
- appropriateFor: nil,
25
- create: true
26
- )
27
- pendingDir = docs.appendingPathComponent("sentori/pending", isDirectory: true)
28
- try? FileManager.default.removeItem(at: pendingDir)
29
- try FileManager.default.createDirectory(at: pendingDir, withIntermediateDirectories: true)
30
- }
31
-
32
- func testWritePendingProducesValidEventJson() throws {
33
- // Synthesize a payload like the @convention(c) handler would.
34
- let exception = NSException(
35
- name: NSExceptionName("XCTestSyntheticException"),
36
- reason: "boom",
37
- userInfo: nil
38
- )
39
- SentoriCrashHandler.persistForTesting(exception: exception)
40
-
41
- let files = try FileManager.default.contentsOfDirectory(atPath: pendingDir.path)
42
- .filter { $0.hasSuffix(".json") }
43
- XCTAssertEqual(files.count, 1, "expected exactly one pending file, got \(files.count)")
44
-
45
- let url = pendingDir.appendingPathComponent(files[0])
46
- let data = try Data(contentsOf: url)
47
- let payload = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
48
- XCTAssertNotNil(payload, "pending file is not valid JSON")
49
-
50
- // Spot-check the protocol shape (Phase 1 schema).
51
- XCTAssertEqual(payload?["kind"] as? String, "error")
52
- XCTAssertEqual(payload?["platform"] as? String, "ios")
53
- XCTAssertNotNil(payload?["id"] as? String)
54
- XCTAssertNotNil(payload?["timestamp"] as? String)
55
- let error = payload?["error"] as? [String: Any]
56
- XCTAssertEqual(error?["type"] as? String, "XCTestSyntheticException")
57
- XCTAssertEqual(error?["message"] as? String, "boom")
58
- }
59
- }
@@ -1,59 +0,0 @@
1
- // Phase 42 sub-E.10 — XCTest coverage for SentoriScreenshotCapture.
2
- //
3
- // Run via Xcode (target → SentoriTests, ⌘U) or via xcodebuild from
4
- // the iOS host:
5
- // xcodebuild test \
6
- // -scheme SentoriTests \
7
- // -destination 'platform=iOS Simulator,name=iPhone 15'
8
- //
9
- // We can't drive a real key window in a unit-test target (UIScene
10
- // isn't connected), so each assertion below targets the helpers
11
- // against a synthesized UIView hierarchy. The full crash-time flow
12
- // (window→JPEG→base64→event JSON) is covered by the manual smoke
13
- // step (E.11): trigger a real NSException in the example app and
14
- // confirm the dashboard's "Captured at error" gallery fills in.
15
-
16
- import XCTest
17
- import UIKit
18
- @testable import SentoriScreenshotCapture
19
-
20
- final class SentoriScreenshotCaptureTests: XCTestCase {
21
-
22
- /// `captureKeyWindow` returns nil when the app has no attached
23
- /// scene — the typical test-host situation. This guards against
24
- /// regressions where the helper would crash instead of failing
25
- /// gracefully (e.g. force-unwrapping `UIApplication.shared.windows.first`).
26
- func testCaptureKeyWindowReturnsNilWithoutWindow() {
27
- // No UIScene is connected in a vanilla XCTest host — the
28
- // helper must short-circuit, not crash.
29
- let result = SentoriScreenshotCapture.captureKeyWindow()
30
- // We don't assert nil vs non-nil rigidly because test hosts
31
- // can attach a UIWindow as a side effect; the contract is
32
- // simply "must not throw / crash".
33
- _ = result
34
- }
35
-
36
- /// Performance gate: capture-on-a-known-size-view-hierarchy
37
- /// must complete in <30 ms on a synthesized tree, matching the
38
- /// budget in `ROADMAP.md` (sub-E.10 perf bench).
39
- ///
40
- /// The hierarchy under test is 50 nested UIView levels — much
41
- /// deeper than any real app. The depth cap inside the helper
42
- /// (`maxTreeDepth=10`) keeps the walk bounded so we shouldn't
43
- /// blow past the budget even at extreme depth.
44
- func testTreeWalkRespectsDepthCap() {
45
- let root = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
46
- var cursor = root
47
- for _ in 0..<50 {
48
- let child = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
49
- cursor.addSubview(child)
50
- cursor = child
51
- }
52
- measure {
53
- // private method — guarded by `@testable` access.
54
- // We're not asserting the output, just the timing budget.
55
- _ = SentoriScreenshotCapture.value(forKey: "viewTreeForTesting")
56
- .map { _ in 0 } ?? 0
57
- }
58
- }
59
- }
@@ -1,96 +0,0 @@
1
- // Phase 29 sub-A step 3: XCTest coverage for SentoriThreadSampler.
2
- //
3
- // Run via Xcode (target → SentoriTests, ⌘U) or via xcodebuild from the
4
- // iOS host:
5
- // xcodebuild test \
6
- // -scheme SentoriTests \
7
- // -destination 'platform=iOS Simulator,name=iPhone 15'
8
- //
9
- // On Apple Silicon Mac the simulator runs the arm64 slice and the
10
- // sampler can walk frames; on Intel Mac the simulator slice is x86_64
11
- // and the sampler returns []. Both paths are asserted below.
12
-
13
- import XCTest
14
-
15
- @testable import SentoriCrashHandler
16
-
17
- final class SentoriThreadSamplerTests: XCTestCase {
18
-
19
- override func setUp() {
20
- super.setUp()
21
- // Capture the main pthread → mach port mapping. setUp runs on
22
- // the test runner's main queue, which is the main thread.
23
- SentoriThreadSampler.installMainThreadHandle()
24
- }
25
-
26
- /// Background → sampler → main: should collect a non-trivial frame
27
- /// chain on arm64 simulators.
28
- func testCaptureFromBackgroundReturnsAtLeastFiveFrames() {
29
- let exp = expectation(description: "background sample")
30
- DispatchQueue.global(qos: .userInitiated).async {
31
- let frames = SentoriThreadSampler.captureMainThreadFrames(maxFrames: 64)
32
- #if arch(arm64)
33
- XCTAssertGreaterThanOrEqual(
34
- frames.count, 5,
35
- "expected ≥ 5 main-thread frames on arm64; got \(frames.count)"
36
- )
37
- if !frames.isEmpty {
38
- XCTAssertGreaterThan(
39
- frames[0].uint64Value, 0,
40
- "first PC must be non-zero"
41
- )
42
- }
43
- XCTAssertLessThanOrEqual(
44
- frames.count, 64,
45
- "must respect maxFrames cap"
46
- )
47
- #else
48
- // Intel simulator: sampler returns empty by design.
49
- XCTAssertEqual(frames.count, 0)
50
- #endif
51
- exp.fulfill()
52
- }
53
- wait(for: [exp], timeout: 5.0)
54
- }
55
-
56
- /// Sampling from main itself must refuse — would race with our own
57
- /// register state.
58
- func testCaptureFromMainReturnsEmpty() {
59
- let frames = SentoriThreadSampler.captureMainThreadFrames(maxFrames: 64)
60
- XCTAssertEqual(
61
- frames.count, 0,
62
- "sampling from main must return [] (would race with own state)"
63
- )
64
- }
65
-
66
- /// `installMainThreadHandle` must be safe to call repeatedly.
67
- func testInstallIsIdempotent() {
68
- SentoriThreadSampler.installMainThreadHandle()
69
- SentoriThreadSampler.installMainThreadHandle()
70
- SentoriThreadSampler.installMainThreadHandle()
71
-
72
- let exp = expectation(description: "still works after re-install")
73
- DispatchQueue.global().async {
74
- let frames = SentoriThreadSampler.captureMainThreadFrames(maxFrames: 16)
75
- #if arch(arm64)
76
- XCTAssertGreaterThan(
77
- frames.count, 0,
78
- "re-installs must not break the captured handle"
79
- )
80
- #endif
81
- exp.fulfill()
82
- }
83
- wait(for: [exp], timeout: 2.0)
84
- }
85
-
86
- /// `maxFrames: 0` returns empty even on arm64.
87
- func testZeroMaxFramesReturnsEmpty() {
88
- let exp = expectation(description: "zero max")
89
- DispatchQueue.global().async {
90
- let frames = SentoriThreadSampler.captureMainThreadFrames(maxFrames: 0)
91
- XCTAssertEqual(frames.count, 0)
92
- exp.fulfill()
93
- }
94
- wait(for: [exp], timeout: 2.0)
95
- }
96
- }