@goliapkg/sentori-react-native 5.4.0 → 5.6.0
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.
- package/android/src/main/java/com/sentori/SentoriForegroundActivity.kt +3 -0
- package/android/src/main/java/com/sentori/SentoriMobileVitals.kt +31 -0
- package/android/src/main/java/com/sentori/SentoriModule.kt +6 -0
- package/ios/SentoriMobileVitals.swift +9 -0
- package/ios/SentoriModule.swift +6 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +12 -0
- package/lib/index.js.map +1 -1
- package/lib/init.d.ts.map +1 -1
- package/lib/init.js +2 -0
- package/lib/init.js.map +1 -1
- package/lib/launch.d.ts +10 -0
- package/lib/launch.d.ts.map +1 -0
- package/lib/launch.js +75 -0
- package/lib/launch.js.map +1 -0
- package/lib/mobile-vitals.d.ts +4 -1
- package/lib/mobile-vitals.d.ts.map +1 -1
- package/lib/mobile-vitals.js +26 -4
- package/lib/mobile-vitals.js.map +1 -1
- package/lib/native.d.ts +4 -0
- package/lib/native.d.ts.map +1 -1
- package/lib/native.js +11 -0
- package/lib/native.js.map +1 -1
- package/lib/transport.js +1 -1
- package/package.json +1 -1
- package/src/__tests__/launch.test.ts +90 -0
- package/src/index.ts +13 -0
- package/src/init.ts +2 -0
- package/src/launch.ts +79 -0
- package/src/mobile-vitals.ts +27 -4
- package/src/native.ts +12 -0
- package/src/transport.ts +1 -1
|
@@ -82,6 +82,9 @@ object SentoriForegroundActivity {
|
|
|
82
82
|
fun set(activity: Activity, source: String) {
|
|
83
83
|
lastActivity = WeakReference(activity)
|
|
84
84
|
lastPath = source
|
|
85
|
+
// First Activity sighting anchors the pre-warm discriminator
|
|
86
|
+
// (idempotent; reflection back-fill counts too).
|
|
87
|
+
SentoriMobileVitals.markFirstActivity()
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
fun current(): Activity? {
|
|
@@ -26,6 +26,14 @@ object SentoriMobileVitals {
|
|
|
26
26
|
|
|
27
27
|
private val jsBridgeReadyAt = AtomicLong(0)
|
|
28
28
|
private val coldStartMs = AtomicLong(-1)
|
|
29
|
+
private val firstActivityAt = AtomicLong(0)
|
|
30
|
+
|
|
31
|
+
/** A process started in the background (FCM data message,
|
|
32
|
+
* JobScheduler, broadcast) and opened by the user minutes later
|
|
33
|
+
* must not report the whole gap as "cold start" — the Firebase
|
|
34
|
+
* Performance bug class. Discriminator: the first Activity was
|
|
35
|
+
* created more than this long after the process start. */
|
|
36
|
+
private const val PREWARM_GAP_MS: Long = 5_000
|
|
29
37
|
|
|
30
38
|
private val slowFrames = AtomicInteger(0)
|
|
31
39
|
private val frozenFrames = AtomicInteger(0)
|
|
@@ -54,6 +62,29 @@ object SentoriMobileVitals {
|
|
|
54
62
|
return if (v < 0) null else v
|
|
55
63
|
}
|
|
56
64
|
|
|
65
|
+
/** Called by [SentoriForegroundActivity] on the first Activity
|
|
66
|
+
* sighting (lifecycle or reflection back-fill). Idempotent. */
|
|
67
|
+
@JvmStatic
|
|
68
|
+
fun markFirstActivity() {
|
|
69
|
+
firstActivityAt.compareAndSet(0, SystemClock.elapsedRealtime())
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** True when the measured "cold start" is a phantom: the process
|
|
73
|
+
* was pre-warmed in the background and the user arrived much
|
|
74
|
+
* later (or no Activity exists at all — headless start). The
|
|
75
|
+
* sample still ships, flagged, so the dashboard can exclude it;
|
|
76
|
+
* the slow-start warn skips it entirely. */
|
|
77
|
+
@JvmStatic
|
|
78
|
+
fun isColdStartPrewarmed(): Boolean {
|
|
79
|
+
val first = firstActivityAt.get()
|
|
80
|
+
if (first == 0L) return true
|
|
81
|
+
return try {
|
|
82
|
+
first - Process.getStartElapsedRealtime() > PREWARM_GAP_MS
|
|
83
|
+
} catch (_: Throwable) {
|
|
84
|
+
false
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
57
88
|
@JvmStatic
|
|
58
89
|
fun startFrameWatch() {
|
|
59
90
|
if (frameWatchRunning) return
|
|
@@ -54,6 +54,12 @@ class SentoriModule : Module() {
|
|
|
54
54
|
Function("getColdStartMs") {
|
|
55
55
|
SentoriMobileVitals.getColdStartMs()
|
|
56
56
|
}
|
|
57
|
+
|
|
58
|
+
// v5.6 — phantom-sample discriminator (ActivePrewarm on iOS,
|
|
59
|
+
// process-to-first-Activity gap on Android).
|
|
60
|
+
Function("isColdStartPrewarmed") {
|
|
61
|
+
SentoriMobileVitals.isColdStartPrewarmed()
|
|
62
|
+
}
|
|
57
63
|
Function("getFrameCounters") {
|
|
58
64
|
SentoriMobileVitals.getFrameCounters()
|
|
59
65
|
}
|
|
@@ -53,6 +53,15 @@ import QuartzCore
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/// iOS 15+ pre-warms processes ahead of user intent; a launch
|
|
57
|
+
/// measured from a pre-warmed anchor is a phantom sample (the
|
|
58
|
+
/// same bug class Firebase Performance shipped and later
|
|
59
|
+
/// guarded). Apple's own discriminator: the ActivePrewarm
|
|
60
|
+
/// environment flag, set only in pre-warmed processes.
|
|
61
|
+
@objc public static func isColdStartPrewarmed() -> Bool {
|
|
62
|
+
return ProcessInfo.processInfo.environment["ActivePrewarm"] == "1"
|
|
63
|
+
}
|
|
64
|
+
|
|
56
65
|
@objc public static func getColdStartMs() -> NSNumber? {
|
|
57
66
|
if let ms = coldStartMs {
|
|
58
67
|
return NSNumber(value: ms)
|
package/ios/SentoriModule.swift
CHANGED
|
@@ -52,6 +52,12 @@ public class SentoriModule: Module {
|
|
|
52
52
|
Function("getColdStartMs") { () -> Double? in
|
|
53
53
|
return SentoriMobileVitals.getColdStartMs()?.doubleValue
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
// v5.6 — phantom-sample discriminator (ActivePrewarm on iOS,
|
|
57
|
+
// process-to-first-Activity gap on Android).
|
|
58
|
+
Function("isColdStartPrewarmed") { () -> Bool in
|
|
59
|
+
return SentoriMobileVitals.isColdStartPrewarmed()
|
|
60
|
+
}
|
|
55
61
|
Function("getFrameCounters") { () -> [String: Any]? in
|
|
56
62
|
return SentoriMobileVitals.getFrameCounters() as? [String: Any]
|
|
57
63
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -31,9 +31,11 @@ export { init } from './init';
|
|
|
31
31
|
export { ErrorBoundary } from './error-boundary';
|
|
32
32
|
export { RageTapCapture } from './rage-tap';
|
|
33
33
|
export { registerMaskQuery } from './mask';
|
|
34
|
+
export { launch } from './launch';
|
|
34
35
|
export { triggerNativeCrash } from './native';
|
|
35
36
|
export { type NavigationRefLike, useTraceNavigation } from './navigation';
|
|
36
37
|
export type { AssertStat, Device, EventData, EventKind, Frame, InitConfig, SentoriApi, SentoriError, Signal, Surface, TraceOptions, User, WireEvent, WirePayload, } from '@goliapkg/sentori-core';
|
|
37
38
|
export { getLogLevel, type LogLevel, setLogLevel, type LogTransport, setLogTransport, } from '@goliapkg/sentori-core';
|
|
39
|
+
export { pushSignal } from '@goliapkg/sentori-core';
|
|
38
40
|
export * as push from './push';
|
|
39
41
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,SAAS,EACT,UAAU,EACV,YAAY,EACZ,IAAI,EACL,MAAM,wBAAwB,CAAC;AAEhC,eAAO,MAAM,OAAO;IAClB,2DAA2D;IAC3D,IAAI,WAAW,UAAU,KAAG,IAAI;IAGhC;;4CAEwC;IACxC,IAAI,MAAM,IAAI,GAAG,IAAI,KAAG,IAAI;IAG5B;8CAC0C;IAC1C,OAAO,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI;IAI/C;sEACkE;IAClE,KAAK,QAAQ,OAAO,SAAS,SAAS,KAAG,MAAM;IAC/C,kDAAkD;IAClD,IAAI,SAAS,MAAM,SAAS,SAAS,KAAG,MAAM;IAC9C;6DACyD;IACzD,KAAK,SAAS,MAAM,SAAS,SAAS,SAAS,YAAY,KAAG,MAAM;IAEpE;;mDAE+C;IAC/C,MAAM,SAAS,MAAM,MAAM,OAAO,SAAS,SAAS,KAAG,MAAM;IAE7D;;qCAEiC;IACjC,KAAK,QAAQ,MAAM,SAAS,SAAS,KAAG,MAAM;CAC/C,CAAC;eAEa,OAAO;AAEtB,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,SAAS,EACT,UAAU,EACV,YAAY,EACZ,IAAI,EACL,MAAM,wBAAwB,CAAC;AAEhC,eAAO,MAAM,OAAO;IAClB,2DAA2D;IAC3D,IAAI,WAAW,UAAU,KAAG,IAAI;IAGhC;;4CAEwC;IACxC,IAAI,MAAM,IAAI,GAAG,IAAI,KAAG,IAAI;IAG5B;8CAC0C;IAC1C,OAAO,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI;IAI/C;sEACkE;IAClE,KAAK,QAAQ,OAAO,SAAS,SAAS,KAAG,MAAM;IAC/C,kDAAkD;IAClD,IAAI,SAAS,MAAM,SAAS,SAAS,KAAG,MAAM;IAC9C;6DACyD;IACzD,KAAK,SAAS,MAAM,SAAS,SAAS,SAAS,YAAY,KAAG,MAAM;IAEpE;;mDAE+C;IAC/C,MAAM,SAAS,MAAM,MAAM,OAAO,SAAS,SAAS,KAAG,MAAM;IAE7D;;qCAEiC;IACjC,KAAK,QAAQ,MAAM,SAAS,SAAS,KAAG,MAAM;CAC/C,CAAC;eAEa,OAAO;AAEtB,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGlC,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,KAAK,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAG1E,YAAY,EACV,UAAU,EACV,MAAM,EACN,SAAS,EACT,SAAS,EACT,KAAK,EACL,UAAU,EACV,UAAU,EACV,YAAY,EACZ,MAAM,EACN,OAAO,EACP,YAAY,EACZ,IAAI,EACJ,SAAS,EACT,WAAW,GACZ,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACL,WAAW,EACX,KAAK,QAAQ,EACb,WAAW,EACX,KAAK,YAAY,EACjB,eAAe,GAChB,MAAM,wBAAwB,CAAC;AAYhC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGpD,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -51,12 +51,24 @@ export { RageTapCapture } from './rage-tap';
|
|
|
51
51
|
// Privacy: register a query returning nativeIDs that must be
|
|
52
52
|
// blacked out in every captured frame (visual replay, screenshots).
|
|
53
53
|
export { registerMaskQuery } from './mask';
|
|
54
|
+
export { launch } from './launch';
|
|
54
55
|
// Dev-panel helper: crash the native layer on purpose to exercise
|
|
55
56
|
// the pending-crash upload pipeline. Not part of the 8-verb surface.
|
|
56
57
|
export { triggerNativeCrash } from './native';
|
|
57
58
|
export { useTraceNavigation } from './navigation';
|
|
58
59
|
// Logger gate, for hosts debugging Sentori itself.
|
|
59
60
|
export { getLogLevel, setLogLevel, setLogTransport, } from '@goliapkg/sentori-core';
|
|
61
|
+
// The signal channel, for hosts feeding their own breadcrumbs into
|
|
62
|
+
// the ring (insight round-4 §4). Same O(1) fire-and-forget the
|
|
63
|
+
// auto signals use; entries ride payload.signals when an error/warn
|
|
64
|
+
// fires and appear on the case timeline + user-events panel.
|
|
65
|
+
//
|
|
66
|
+
// Field conventions the dashboard understands:
|
|
67
|
+
// pushSignal('http', { method, url, status, ms }) — a request's
|
|
68
|
+
// outcome (host's own interceptor is the right producer; the
|
|
69
|
+
// SDK deliberately does not monkey-patch fetch/XHR)
|
|
70
|
+
// any other kind renders with its data as `k=v` pairs.
|
|
71
|
+
export { pushSignal } from '@goliapkg/sentori-core';
|
|
60
72
|
// Push namespace — carried as-is; outside the v1 acceptance surface.
|
|
61
73
|
export * as push from './push';
|
|
62
74
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,uEAAuE;AACvE,oEAAoE;AACpE,8CAA8C;AAC9C,EAAE;AACF,gEAAgE;AAChE,oEAAoE;AACpE,qEAAqE;AACrE,8DAA8D;AAE9D,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAShC,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,2DAA2D;IAC3D,IAAI,EAAE,CAAC,MAAkB,EAAQ,EAAE;QACjC,IAAI,CAAC,MAAM,CAAC,CAAC;IACf,CAAC;IACD;;4CAEwC;IACxC,IAAI,EAAE,CAAC,CAAc,EAAQ,EAAE;QAC7B,OAAO,CAAC,CAAC,CAAC,CAAC;IACb,CAAC;IACD;8CAC0C;IAC1C,OAAO,EAAE,CAAC,KAA8B,EAAQ,EAAE;QAChD,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;IAED;sEACkE;IAClE,KAAK,EAAE,CAAC,GAAY,EAAE,IAAgB,EAAU,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC;IACzE,kDAAkD;IAClD,IAAI,EAAE,CAAC,IAAY,EAAE,IAAgB,EAAU,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;IACxE;6DACyD;IACzD,KAAK,EAAE,CAAC,IAAY,EAAE,IAAgB,EAAE,IAAmB,EAAU,EAAE,CACrE,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IAC/B;;mDAE+C;IAC/C,MAAM,EAAE,CAAC,IAAY,EAAE,EAAW,EAAE,IAAgB,EAAU,EAAE,CAC9D,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC;IAC9B;;qCAEiC;IACjC,KAAK,EAAE,CAAC,GAAW,EAAE,IAAgB,EAAU,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC;CACzE,CAAC;AAEF,eAAe,OAAO,CAAC;AAEvB,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,6DAA6D;AAC7D,oEAAoE;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,kEAAkE;AAClE,qEAAqE;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAA0B,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAoB1E,mDAAmD;AACnD,OAAO,EACL,WAAW,EAEX,WAAW,EAEX,eAAe,GAChB,MAAM,wBAAwB,CAAC;AAEhC,qEAAqE;AACrE,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,uEAAuE;AACvE,oEAAoE;AACpE,8CAA8C;AAC9C,EAAE;AACF,gEAAgE;AAChE,oEAAoE;AACpE,qEAAqE;AACrE,8DAA8D;AAE9D,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAShC,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,2DAA2D;IAC3D,IAAI,EAAE,CAAC,MAAkB,EAAQ,EAAE;QACjC,IAAI,CAAC,MAAM,CAAC,CAAC;IACf,CAAC;IACD;;4CAEwC;IACxC,IAAI,EAAE,CAAC,CAAc,EAAQ,EAAE;QAC7B,OAAO,CAAC,CAAC,CAAC,CAAC;IACb,CAAC;IACD;8CAC0C;IAC1C,OAAO,EAAE,CAAC,KAA8B,EAAQ,EAAE;QAChD,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;IAED;sEACkE;IAClE,KAAK,EAAE,CAAC,GAAY,EAAE,IAAgB,EAAU,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC;IACzE,kDAAkD;IAClD,IAAI,EAAE,CAAC,IAAY,EAAE,IAAgB,EAAU,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;IACxE;6DACyD;IACzD,KAAK,EAAE,CAAC,IAAY,EAAE,IAAgB,EAAE,IAAmB,EAAU,EAAE,CACrE,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IAC/B;;mDAE+C;IAC/C,MAAM,EAAE,CAAC,IAAY,EAAE,EAAW,EAAE,IAAgB,EAAU,EAAE,CAC9D,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC;IAC9B;;qCAEiC;IACjC,KAAK,EAAE,CAAC,GAAW,EAAE,IAAgB,EAAU,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC;CACzE,CAAC;AAEF,eAAe,OAAO,CAAC;AAEvB,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,6DAA6D;AAC7D,oEAAoE;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,kEAAkE;AAClE,qEAAqE;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAA0B,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAoB1E,mDAAmD;AACnD,OAAO,EACL,WAAW,EAEX,WAAW,EAEX,eAAe,GAChB,MAAM,wBAAwB,CAAC;AAEhC,mEAAmE;AACnE,+DAA+D;AAC/D,oEAAoE;AACpE,6DAA6D;AAC7D,EAAE;AACF,+CAA+C;AAC/C,mEAAmE;AACnE,iEAAiE;AACjE,wDAAwD;AACxD,yDAAyD;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,qEAAqE;AACrE,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC"}
|
package/lib/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAmBzD,eAAO,MAAM,IAAI,0CAwFf,CAAC;AAGH,eAAO,MAAM,eAAe,QAAO,IAElC,CAAC"}
|
package/lib/init.js
CHANGED
|
@@ -11,6 +11,7 @@ import { installNetworkHandler } from './handlers/network';
|
|
|
11
11
|
import { installPromiseHandler } from './handlers/promise';
|
|
12
12
|
import { startLongTaskMonitor } from './long-task-monitor';
|
|
13
13
|
import { checkColdStart } from './mobile-vitals';
|
|
14
|
+
import { armLaunch } from './launch';
|
|
14
15
|
import { markNativeJsBridgeReady, setNativeConfig } from './native';
|
|
15
16
|
import { shipNativePending } from './native-pending';
|
|
16
17
|
import { drainReplay, startReplay } from './replay';
|
|
@@ -99,6 +100,7 @@ export const init = safeFn('init', (config) => {
|
|
|
99
100
|
markNativeJsBridgeReady();
|
|
100
101
|
startTransport();
|
|
101
102
|
checkColdStart(config.detect?.slowColdStart !== false);
|
|
103
|
+
armLaunch();
|
|
102
104
|
void shipNativePending().catch(() => undefined);
|
|
103
105
|
void drainOfflineQueue();
|
|
104
106
|
});
|
package/lib/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
package/lib/launch.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Called from init(): anchors the JS side of the staged timeline. */
|
|
2
|
+
export declare function armLaunch(): void;
|
|
3
|
+
/** The staged-launch surface: `sentori.launch.mark/complete`. */
|
|
4
|
+
export declare const launch: {
|
|
5
|
+
complete: () => void | undefined;
|
|
6
|
+
mark: (name: string) => void | undefined;
|
|
7
|
+
};
|
|
8
|
+
/** Test-only. */
|
|
9
|
+
export declare function __resetLaunchForTests(): void;
|
|
10
|
+
//# sourceMappingURL=launch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../src/launch.ts"],"names":[],"mappings":"AA8BA,sEAAsE;AACtE,wBAAgB,SAAS,IAAI,IAAI,CAEhC;AAqCD,iEAAiE;AACjE,eAAO,MAAM,MAAM;;;CAAqB,CAAC;AAEzC,iBAAiB;AACjB,wBAAgB,qBAAqB,IAAI,IAAI,CAI5C"}
|
package/lib/launch.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Staged launch measurement (insight QIP-8 #2).
|
|
2
|
+
//
|
|
3
|
+
// One scalar closing at JS init() cannot represent user-perceived
|
|
4
|
+
// launch: apps that defer init() off the critical path get a number
|
|
5
|
+
// that is neither time-to-first-frame nor time-to-usable, and
|
|
6
|
+
// everything after init (bootstrap, splash-hide, first data) is
|
|
7
|
+
// invisible. This is the staged shape instead:
|
|
8
|
+
//
|
|
9
|
+
// native anchors (process start → JS ready, from mobile vitals)
|
|
10
|
+
// + sentori.launch.mark(name) — app-defined waypoints
|
|
11
|
+
// + sentori.launch.complete() — the app's own "usable now"
|
|
12
|
+
//
|
|
13
|
+
// complete() emits ONE `app.launch` trace event with segment
|
|
14
|
+
// durations — it rides the existing event channel (batched, quiet
|
|
15
|
+
// network posture untouched) and aggregates like any trace. Apps
|
|
16
|
+
// that never call the marks keep today's behaviour exactly: no
|
|
17
|
+
// marks, no event.
|
|
18
|
+
//
|
|
19
|
+
// Same iron-rule surface as every verb: synchronous, O(1), never
|
|
20
|
+
// throws (safeFn), init-failure degrades to no-op.
|
|
21
|
+
import { safeFn } from '@goliapkg/sentori-core';
|
|
22
|
+
import { getColdStartMs, isColdStartPrewarmed } from './mobile-vitals';
|
|
23
|
+
import { trace } from './verbs';
|
|
24
|
+
let _armedAt = null;
|
|
25
|
+
let _marks = [];
|
|
26
|
+
let _completed = false;
|
|
27
|
+
/** Called from init(): anchors the JS side of the staged timeline. */
|
|
28
|
+
export function armLaunch() {
|
|
29
|
+
if (_armedAt === null)
|
|
30
|
+
_armedAt = Date.now();
|
|
31
|
+
}
|
|
32
|
+
const mark = safeFn('launch.mark', (name) => {
|
|
33
|
+
if (_completed || typeof name !== 'string' || name.length === 0)
|
|
34
|
+
return;
|
|
35
|
+
if (_marks.length >= 32)
|
|
36
|
+
return; // bounded, like every buffer
|
|
37
|
+
_marks.push({ at: Date.now(), name });
|
|
38
|
+
});
|
|
39
|
+
const complete = safeFn('launch.complete', () => {
|
|
40
|
+
if (_completed || _armedAt === null)
|
|
41
|
+
return;
|
|
42
|
+
_completed = true;
|
|
43
|
+
const completeAt = Date.now();
|
|
44
|
+
const coldMs = getColdStartMs();
|
|
45
|
+
const prewarmed = isColdStartPrewarmed();
|
|
46
|
+
// Segment durations, in narrative order: the native span first
|
|
47
|
+
// (process start → JS ready), then each waypoint since init, then
|
|
48
|
+
// the tail up to complete().
|
|
49
|
+
const segments = [];
|
|
50
|
+
if (coldMs !== null)
|
|
51
|
+
segments.push({ ms: Math.round(coldMs), name: 'native' });
|
|
52
|
+
let prev = _armedAt;
|
|
53
|
+
for (const m of _marks.sort((a, b) => a.at - b.at)) {
|
|
54
|
+
segments.push({ ms: Math.max(0, m.at - prev), name: m.name });
|
|
55
|
+
prev = m.at;
|
|
56
|
+
}
|
|
57
|
+
segments.push({ ms: Math.max(0, completeAt - prev), name: 'complete' });
|
|
58
|
+
const jsMs = completeAt - _armedAt;
|
|
59
|
+
const totalMs = Math.round((coldMs ?? 0) + jsMs);
|
|
60
|
+
trace('app.launch', {
|
|
61
|
+
totalMs,
|
|
62
|
+
...(coldMs !== null ? { coldStartMs: Math.round(coldMs) } : { jsOnly: true }),
|
|
63
|
+
...(prewarmed ? { prewarmed: true } : {}),
|
|
64
|
+
segments,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
/** The staged-launch surface: `sentori.launch.mark/complete`. */
|
|
68
|
+
export const launch = { complete, mark };
|
|
69
|
+
/** Test-only. */
|
|
70
|
+
export function __resetLaunchForTests() {
|
|
71
|
+
_armedAt = null;
|
|
72
|
+
_marks = [];
|
|
73
|
+
_completed = false;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=launch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch.js","sourceRoot":"","sources":["../src/launch.ts"],"names":[],"mappings":"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"}
|
package/lib/mobile-vitals.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/** Read the native-side cold start measurement once. Cached. */
|
|
2
2
|
export declare function getColdStartMs(): null | number;
|
|
3
|
-
/**
|
|
3
|
+
/** Whether the cached measurement is a pre-warmed phantom. Only
|
|
4
|
+
* meaningful after getColdStartMs() ran. */
|
|
5
|
+
export declare function isColdStartPrewarmed(): boolean;
|
|
6
|
+
/** Called once from init(): signal always, warn when slow AND real. */
|
|
4
7
|
export declare function checkColdStart(detectEnabled: boolean): void;
|
|
5
8
|
/** Test-only. */
|
|
6
9
|
export declare function __resetMobileVitalsForTests(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mobile-vitals.d.ts","sourceRoot":"","sources":["../src/mobile-vitals.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mobile-vitals.d.ts","sourceRoot":"","sources":["../src/mobile-vitals.ts"],"names":[],"mappings":"AA+BA,gEAAgE;AAChE,wBAAgB,cAAc,IAAI,IAAI,GAAG,MAAM,CAU9C;AAED;6CAC6C;AAC7C,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,aAAa,EAAE,OAAO,GAAG,IAAI,CAY3D;AAED,iBAAiB;AACjB,wBAAgB,2BAA2B,IAAI,IAAI,CAIlD"}
|
package/lib/mobile-vitals.js
CHANGED
|
@@ -6,13 +6,23 @@
|
|
|
6
6
|
// init() calls checkColdStart() once. > 3 s ⇒ one `warn` event,
|
|
7
7
|
// scenario `slow_cold_start`, with the measured ms. Always pushes a
|
|
8
8
|
// `lifecycle` signal so even a fast start shows on the timeline.
|
|
9
|
+
//
|
|
10
|
+
// Pre-warmed processes (iOS ActivePrewarm; Android background start
|
|
11
|
+
// — FCM data message, JobScheduler — with the first Activity
|
|
12
|
+
// arriving much later) produce phantom samples: the measured span
|
|
13
|
+
// is mostly idle background time, not launch. Those samples still
|
|
14
|
+
// ship, flagged `prewarmed: true`, so the field data stays honest
|
|
15
|
+
// and slicable — but they never fire the slow-start warn (insight
|
|
16
|
+
// QIP-8 #1; the Firebase Performance bug class).
|
|
17
|
+
//
|
|
9
18
|
// Graceful no-op when the native module isn't linked (Expo Go,
|
|
10
19
|
// tests).
|
|
11
20
|
import { pushSignal } from '@goliapkg/sentori-core';
|
|
12
|
-
import { getNativeColdStartMs } from './native';
|
|
21
|
+
import { getNativeColdStartMs, getNativeColdStartPrewarmed } from './native';
|
|
13
22
|
import { warnDetected } from './verbs';
|
|
14
23
|
const SLOW_COLD_START_MS = 3_000;
|
|
15
24
|
let _coldStartMs = null;
|
|
25
|
+
let _coldStartPrewarmed = false;
|
|
16
26
|
let _coldStartCaptured = false;
|
|
17
27
|
/** Read the native-side cold start measurement once. Cached. */
|
|
18
28
|
export function getColdStartMs() {
|
|
@@ -21,19 +31,30 @@ export function getColdStartMs() {
|
|
|
21
31
|
_coldStartCaptured = true;
|
|
22
32
|
try {
|
|
23
33
|
_coldStartMs = getNativeColdStartMs();
|
|
34
|
+
_coldStartPrewarmed = getNativeColdStartPrewarmed();
|
|
24
35
|
}
|
|
25
36
|
catch {
|
|
26
37
|
_coldStartMs = null;
|
|
27
38
|
}
|
|
28
39
|
return _coldStartMs;
|
|
29
40
|
}
|
|
30
|
-
/**
|
|
41
|
+
/** Whether the cached measurement is a pre-warmed phantom. Only
|
|
42
|
+
* meaningful after getColdStartMs() ran. */
|
|
43
|
+
export function isColdStartPrewarmed() {
|
|
44
|
+
return _coldStartPrewarmed;
|
|
45
|
+
}
|
|
46
|
+
/** Called once from init(): signal always, warn when slow AND real. */
|
|
31
47
|
export function checkColdStart(detectEnabled) {
|
|
32
48
|
const ms = getColdStartMs();
|
|
33
49
|
if (ms === null)
|
|
34
50
|
return;
|
|
35
|
-
|
|
36
|
-
|
|
51
|
+
const prewarmed = isColdStartPrewarmed();
|
|
52
|
+
pushSignal('lifecycle', {
|
|
53
|
+
phase: 'cold_start',
|
|
54
|
+
ms,
|
|
55
|
+
...(prewarmed ? { prewarmed: true } : {}),
|
|
56
|
+
});
|
|
57
|
+
if (detectEnabled && !prewarmed && ms > SLOW_COLD_START_MS) {
|
|
37
58
|
warnDetected('slow_cold_start', {}, { coldStartMs: ms, thresholdMs: SLOW_COLD_START_MS });
|
|
38
59
|
}
|
|
39
60
|
}
|
|
@@ -41,5 +62,6 @@ export function checkColdStart(detectEnabled) {
|
|
|
41
62
|
export function __resetMobileVitalsForTests() {
|
|
42
63
|
_coldStartCaptured = false;
|
|
43
64
|
_coldStartMs = null;
|
|
65
|
+
_coldStartPrewarmed = false;
|
|
44
66
|
}
|
|
45
67
|
//# sourceMappingURL=mobile-vitals.js.map
|
package/lib/mobile-vitals.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mobile-vitals.js","sourceRoot":"","sources":["../src/mobile-vitals.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,cAAc;AACd,EAAE;AACF,6DAA6D;AAC7D,iEAAiE;AACjE,gEAAgE;AAChE,oEAAoE;AACpE,iEAAiE;AACjE,+DAA+D;AAC/D,UAAU;AAEV,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"mobile-vitals.js","sourceRoot":"","sources":["../src/mobile-vitals.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,cAAc;AACd,EAAE;AACF,6DAA6D;AAC7D,iEAAiE;AACjE,gEAAgE;AAChE,oEAAoE;AACpE,iEAAiE;AACjE,EAAE;AACF,oEAAoE;AACpE,6DAA6D;AAC7D,kEAAkE;AAClE,kEAAkE;AAClE,kEAAkE;AAClE,kEAAkE;AAClE,iDAAiD;AACjD,EAAE;AACF,+DAA+D;AAC/D,UAAU;AAEV,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,kBAAkB,GAAG,KAAK,CAAC;AAEjC,IAAI,YAAY,GAAkB,IAAI,CAAC;AACvC,IAAI,mBAAmB,GAAG,KAAK,CAAC;AAChC,IAAI,kBAAkB,GAAG,KAAK,CAAC;AAE/B,gEAAgE;AAChE,MAAM,UAAU,cAAc;IAC5B,IAAI,kBAAkB;QAAE,OAAO,YAAY,CAAC;IAC5C,kBAAkB,GAAG,IAAI,CAAC;IAC1B,IAAI,CAAC;QACH,YAAY,GAAG,oBAAoB,EAAE,CAAC;QACtC,mBAAmB,GAAG,2BAA2B,EAAE,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,YAAY,GAAG,IAAI,CAAC;IACtB,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;6CAC6C;AAC7C,MAAM,UAAU,oBAAoB;IAClC,OAAO,mBAAmB,CAAC;AAC7B,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,cAAc,CAAC,aAAsB;IACnD,MAAM,EAAE,GAAG,cAAc,EAAE,CAAC;IAC5B,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO;IACxB,MAAM,SAAS,GAAG,oBAAoB,EAAE,CAAC;IACzC,UAAU,CAAC,WAAW,EAAE;QACtB,KAAK,EAAE,YAAY;QACnB,EAAE;QACF,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1C,CAAC,CAAC;IACH,IAAI,aAAa,IAAI,CAAC,SAAS,IAAI,EAAE,GAAG,kBAAkB,EAAE,CAAC;QAC3D,YAAY,CAAC,iBAAiB,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAC5F,CAAC;AACH,CAAC;AAED,iBAAiB;AACjB,MAAM,UAAU,2BAA2B;IACzC,kBAAkB,GAAG,KAAK,CAAC;IAC3B,YAAY,GAAG,IAAI,CAAC;IACpB,mBAAmB,GAAG,KAAK,CAAC;AAC9B,CAAC"}
|
package/lib/native.d.ts
CHANGED
|
@@ -58,6 +58,10 @@ export declare function stopAnrWatchdog(): void;
|
|
|
58
58
|
export declare function markNativeJsBridgeReady(): void;
|
|
59
59
|
/** v0.9.4 #1 — read cold start ms once. null when native unavailable. */
|
|
60
60
|
export declare function getNativeColdStartMs(): null | number;
|
|
61
|
+
/** v5.6 — phantom-sample discriminator: the process was pre-warmed
|
|
62
|
+
* (iOS ActivePrewarm; Android background start with the first
|
|
63
|
+
* Activity arriving much later). Older binaries: false. */
|
|
64
|
+
export declare function getNativeColdStartPrewarmed(): boolean;
|
|
61
65
|
/** v0.9.4 #1 — read slow/frozen frame counters since last reset. */
|
|
62
66
|
export declare function getNativeFrameCounters(): null | {
|
|
63
67
|
frozen: number;
|
package/lib/native.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAmMH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd,GAAG,IAAI,CAMP;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAQ5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAMzC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE;IACzC,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,GAAG,IAAI,CAMP;AAID;gEACgE;AAChE,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAQ5D;AAED;yCACyC;AACzC,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAQpE;AAED,wBAAgB,YAAY,IAAI,IAAI,CAMnC;AAED,wBAAgB,cAAc,IAAI,IAAI,CAMrC;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IAC7C,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CACrC,CAAA;AAED;8CAC8C;AAC9C,wBAAsB,cAAc,IAAI,OAAO,CAAC,cAAc,CAAC,CAU9D;AAED,wBAAgB,eAAe,IAAI,IAAI,CAMtC;AAED,+DAA+D;AAC/D,wBAAgB,uBAAuB,IAAI,IAAI,CAM9C;AAED,yEAAyE;AACzE,wBAAgB,oBAAoB,IAAI,IAAI,GAAG,MAAM,CAOpD;AAED;;4DAE4D;AAC5D,wBAAgB,2BAA2B,IAAI,OAAO,CAMrD;AAED,oEAAoE;AACpE,wBAAgB,sBAAsB,IAAI,IAAI,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAMhF;AAED,iEAAiE;AACjE,wBAAgB,wBAAwB,IAAI,IAAI,CAM/C;AAED;;yBAEyB;AACzB,wBAAgB,wBAAwB,IAAI,IAAI,GAAG;IACjD,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB,CAMA;AAiBD,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,EAAE,EACnB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB/E;AAED,wBAAsB,+BAA+B,CACnD,SAAS,EAAE,MAAM,EAAE,GAClB,OAAO,CAAC,IAAI,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBvD;AAED;;sEAEsE;AACtE,wBAAgB,uBAAuB,IAAI;IACzC,KAAK,EAAE,OAAO,CAAA;IACd,mBAAmB,EAAE,OAAO,CAAA;IAC5B,iBAAiB,EAAE,OAAO,CAAA;CAC3B,CAOA;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,IAAI;IACtC,SAAS,EAAE,OAAO,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB;;+DAE2D;IAC3D,YAAY,EAAE,MAAM,CAAA;IACpB,+DAA+D;IAC/D,aAAa,EAAE,MAAM,CAAA;IACrB;2CACuC;IACvC,UAAU,EAAE,MAAM,CAAA;IAClB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC7B,CA+CA;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,qBAAqB,IAAI;IACvC,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC7B,CAeA"}
|
package/lib/native.js
CHANGED
|
@@ -167,6 +167,17 @@ export function getNativeColdStartMs() {
|
|
|
167
167
|
return null;
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
+
/** v5.6 — phantom-sample discriminator: the process was pre-warmed
|
|
171
|
+
* (iOS ActivePrewarm; Android background start with the first
|
|
172
|
+
* Activity arriving much later). Older binaries: false. */
|
|
173
|
+
export function getNativeColdStartPrewarmed() {
|
|
174
|
+
try {
|
|
175
|
+
return native()?.isColdStartPrewarmed?.() === true;
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
170
181
|
/** v0.9.4 #1 — read slow/frozen frame counters since last reset. */
|
|
171
182
|
export function getNativeFrameCounters() {
|
|
172
183
|
try {
|
package/lib/native.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native.js","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"native.js","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"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"}
|
package/lib/transport.js
CHANGED
|
@@ -15,7 +15,7 @@ const MAX_RETRY = 3;
|
|
|
15
15
|
const STORAGE_KEY = '@sentori/pending';
|
|
16
16
|
const MAX_PERSISTED = 1000;
|
|
17
17
|
// Pinned to package.json by a test — bump both together.
|
|
18
|
-
const SDK_VERSION = '5.
|
|
18
|
+
const SDK_VERSION = '5.6.0';
|
|
19
19
|
let _queue = [];
|
|
20
20
|
let _assertStats = new Map();
|
|
21
21
|
let _flushTimer = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goliapkg/sentori-react-native",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.6.0",
|
|
4
4
|
"description": "Sentori SDK for React Native \u2014 JS-layer error capture, native crash handlers (iOS / Android), batched transport, fetch + react-navigation tracing.",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"author": "GOLIA K.K. <takagi@golia.jp> (https://golia.jp)",
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Staged launch measurement (QIP-8 #2): marks + complete emit ONE
|
|
2
|
+
// app.launch trace with segment durations; never before arm, never
|
|
3
|
+
// twice, never a throw.
|
|
4
|
+
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
|
|
6
|
+
|
|
7
|
+
import { clearSignals } from '@goliapkg/sentori-core';
|
|
8
|
+
|
|
9
|
+
import { __resetForTests as resetConfig, setConfig } from '../config';
|
|
10
|
+
import { __resetLaunchForTests, armLaunch, launch } from '../launch';
|
|
11
|
+
import { __resetMobileVitalsForTests } from '../mobile-vitals';
|
|
12
|
+
import { __resetForTests as resetScope } from '../scope';
|
|
13
|
+
import {
|
|
14
|
+
__peekQueue,
|
|
15
|
+
__resetForTests as resetTransport,
|
|
16
|
+
startTransport,
|
|
17
|
+
} from '../transport';
|
|
18
|
+
|
|
19
|
+
const baseConfig = {
|
|
20
|
+
token: 'st_test',
|
|
21
|
+
ingestUrl: 'http://localhost:18080',
|
|
22
|
+
release: 'app@1.0.0',
|
|
23
|
+
environment: 'test',
|
|
24
|
+
enabled: true,
|
|
25
|
+
detect: { rageTap: true, longFreeze: true, slowColdStart: true, slowApi: false },
|
|
26
|
+
replaySeconds: 30,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
describe('launch marks', () => {
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
resetTransport();
|
|
32
|
+
resetConfig();
|
|
33
|
+
resetScope();
|
|
34
|
+
clearSignals();
|
|
35
|
+
__resetLaunchForTests();
|
|
36
|
+
__resetMobileVitalsForTests();
|
|
37
|
+
setConfig(baseConfig);
|
|
38
|
+
startTransport();
|
|
39
|
+
});
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
resetTransport();
|
|
42
|
+
resetConfig();
|
|
43
|
+
resetScope();
|
|
44
|
+
clearSignals();
|
|
45
|
+
__resetLaunchForTests();
|
|
46
|
+
__resetMobileVitalsForTests();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const launchEvents = () =>
|
|
50
|
+
__peekQueue().filter((e) => e.kind === 'trace' && e.name === 'app.launch');
|
|
51
|
+
|
|
52
|
+
it('complete() before arm is a no-op (init never ran)', () => {
|
|
53
|
+
launch.mark('bootstrap');
|
|
54
|
+
launch.complete();
|
|
55
|
+
expect(launchEvents()).toHaveLength(0);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('arm → marks → complete emits one app.launch trace with ordered segments', () => {
|
|
59
|
+
armLaunch();
|
|
60
|
+
launch.mark('bootstrap');
|
|
61
|
+
launch.mark('first-tree');
|
|
62
|
+
launch.complete();
|
|
63
|
+
const evs = launchEvents();
|
|
64
|
+
expect(evs).toHaveLength(1);
|
|
65
|
+
const data = (evs[0]!.payload as { data?: Record<string, unknown> })?.data ?? {};
|
|
66
|
+
expect(typeof data.totalMs).toBe('number');
|
|
67
|
+
const segs = data.segments as { ms: number; name: string }[];
|
|
68
|
+
// no native module in tests → jsOnly, segments are the marks + tail
|
|
69
|
+
expect(data.jsOnly).toBe(true);
|
|
70
|
+
expect(segs.map((s) => s.name)).toEqual(['bootstrap', 'first-tree', 'complete']);
|
|
71
|
+
for (const s of segs) expect(s.ms).toBeGreaterThanOrEqual(0);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('complete() is once — the second call emits nothing', () => {
|
|
75
|
+
armLaunch();
|
|
76
|
+
launch.complete();
|
|
77
|
+
launch.mark('late'); // ignored after complete
|
|
78
|
+
launch.complete();
|
|
79
|
+
expect(launchEvents()).toHaveLength(1);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('never throws on garbage input', () => {
|
|
83
|
+
armLaunch();
|
|
84
|
+
expect(() => {
|
|
85
|
+
launch.mark(undefined as unknown as string);
|
|
86
|
+
launch.mark('');
|
|
87
|
+
launch.complete();
|
|
88
|
+
}).not.toThrow();
|
|
89
|
+
});
|
|
90
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -65,6 +65,7 @@ export { RageTapCapture } from './rage-tap';
|
|
|
65
65
|
// Privacy: register a query returning nativeIDs that must be
|
|
66
66
|
// blacked out in every captured frame (visual replay, screenshots).
|
|
67
67
|
export { registerMaskQuery } from './mask';
|
|
68
|
+
export { launch } from './launch';
|
|
68
69
|
// Dev-panel helper: crash the native layer on purpose to exercise
|
|
69
70
|
// the pending-crash upload pipeline. Not part of the 8-verb surface.
|
|
70
71
|
export { triggerNativeCrash } from './native';
|
|
@@ -97,5 +98,17 @@ export {
|
|
|
97
98
|
setLogTransport,
|
|
98
99
|
} from '@goliapkg/sentori-core';
|
|
99
100
|
|
|
101
|
+
// The signal channel, for hosts feeding their own breadcrumbs into
|
|
102
|
+
// the ring (insight round-4 §4). Same O(1) fire-and-forget the
|
|
103
|
+
// auto signals use; entries ride payload.signals when an error/warn
|
|
104
|
+
// fires and appear on the case timeline + user-events panel.
|
|
105
|
+
//
|
|
106
|
+
// Field conventions the dashboard understands:
|
|
107
|
+
// pushSignal('http', { method, url, status, ms }) — a request's
|
|
108
|
+
// outcome (host's own interceptor is the right producer; the
|
|
109
|
+
// SDK deliberately does not monkey-patch fetch/XHR)
|
|
110
|
+
// any other kind renders with its data as `k=v` pairs.
|
|
111
|
+
export { pushSignal } from '@goliapkg/sentori-core';
|
|
112
|
+
|
|
100
113
|
// Push namespace — carried as-is; outside the v1 acceptance surface.
|
|
101
114
|
export * as push from './push';
|
package/src/init.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { installNetworkHandler } from './handlers/network';
|
|
|
14
14
|
import { installPromiseHandler } from './handlers/promise';
|
|
15
15
|
import { startLongTaskMonitor } from './long-task-monitor';
|
|
16
16
|
import { checkColdStart } from './mobile-vitals';
|
|
17
|
+
import { armLaunch } from './launch';
|
|
17
18
|
import { markNativeJsBridgeReady, setNativeConfig } from './native';
|
|
18
19
|
import { shipNativePending } from './native-pending';
|
|
19
20
|
import { drainReplay, startReplay } from './replay';
|
|
@@ -107,6 +108,7 @@ export const init = safeFn('init', (config: InitConfig): void => {
|
|
|
107
108
|
|
|
108
109
|
startTransport();
|
|
109
110
|
checkColdStart(config.detect?.slowColdStart !== false);
|
|
111
|
+
armLaunch();
|
|
110
112
|
void shipNativePending().catch(() => undefined);
|
|
111
113
|
void drainOfflineQueue();
|
|
112
114
|
});
|
package/src/launch.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Staged launch measurement (insight QIP-8 #2).
|
|
2
|
+
//
|
|
3
|
+
// One scalar closing at JS init() cannot represent user-perceived
|
|
4
|
+
// launch: apps that defer init() off the critical path get a number
|
|
5
|
+
// that is neither time-to-first-frame nor time-to-usable, and
|
|
6
|
+
// everything after init (bootstrap, splash-hide, first data) is
|
|
7
|
+
// invisible. This is the staged shape instead:
|
|
8
|
+
//
|
|
9
|
+
// native anchors (process start → JS ready, from mobile vitals)
|
|
10
|
+
// + sentori.launch.mark(name) — app-defined waypoints
|
|
11
|
+
// + sentori.launch.complete() — the app's own "usable now"
|
|
12
|
+
//
|
|
13
|
+
// complete() emits ONE `app.launch` trace event with segment
|
|
14
|
+
// durations — it rides the existing event channel (batched, quiet
|
|
15
|
+
// network posture untouched) and aggregates like any trace. Apps
|
|
16
|
+
// that never call the marks keep today's behaviour exactly: no
|
|
17
|
+
// marks, no event.
|
|
18
|
+
//
|
|
19
|
+
// Same iron-rule surface as every verb: synchronous, O(1), never
|
|
20
|
+
// throws (safeFn), init-failure degrades to no-op.
|
|
21
|
+
|
|
22
|
+
import { safeFn } from '@goliapkg/sentori-core';
|
|
23
|
+
|
|
24
|
+
import { getColdStartMs, isColdStartPrewarmed } from './mobile-vitals';
|
|
25
|
+
import { trace } from './verbs';
|
|
26
|
+
|
|
27
|
+
let _armedAt: null | number = null;
|
|
28
|
+
let _marks: { name: string; at: number }[] = [];
|
|
29
|
+
let _completed = false;
|
|
30
|
+
|
|
31
|
+
/** Called from init(): anchors the JS side of the staged timeline. */
|
|
32
|
+
export function armLaunch(): void {
|
|
33
|
+
if (_armedAt === null) _armedAt = Date.now();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const mark = safeFn('launch.mark', (name: string): void => {
|
|
37
|
+
if (_completed || typeof name !== 'string' || name.length === 0) return;
|
|
38
|
+
if (_marks.length >= 32) return; // bounded, like every buffer
|
|
39
|
+
_marks.push({ at: Date.now(), name });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const complete = safeFn('launch.complete', (): void => {
|
|
43
|
+
if (_completed || _armedAt === null) return;
|
|
44
|
+
_completed = true;
|
|
45
|
+
const completeAt = Date.now();
|
|
46
|
+
const coldMs = getColdStartMs();
|
|
47
|
+
const prewarmed = isColdStartPrewarmed();
|
|
48
|
+
|
|
49
|
+
// Segment durations, in narrative order: the native span first
|
|
50
|
+
// (process start → JS ready), then each waypoint since init, then
|
|
51
|
+
// the tail up to complete().
|
|
52
|
+
const segments: { ms: number; name: string }[] = [];
|
|
53
|
+
if (coldMs !== null) segments.push({ ms: Math.round(coldMs), name: 'native' });
|
|
54
|
+
let prev = _armedAt;
|
|
55
|
+
for (const m of _marks.sort((a, b) => a.at - b.at)) {
|
|
56
|
+
segments.push({ ms: Math.max(0, m.at - prev), name: m.name });
|
|
57
|
+
prev = m.at;
|
|
58
|
+
}
|
|
59
|
+
segments.push({ ms: Math.max(0, completeAt - prev), name: 'complete' });
|
|
60
|
+
|
|
61
|
+
const jsMs = completeAt - _armedAt;
|
|
62
|
+
const totalMs = Math.round((coldMs ?? 0) + jsMs);
|
|
63
|
+
trace('app.launch', {
|
|
64
|
+
totalMs,
|
|
65
|
+
...(coldMs !== null ? { coldStartMs: Math.round(coldMs) } : { jsOnly: true }),
|
|
66
|
+
...(prewarmed ? { prewarmed: true } : {}),
|
|
67
|
+
segments,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/** The staged-launch surface: `sentori.launch.mark/complete`. */
|
|
72
|
+
export const launch = { complete, mark };
|
|
73
|
+
|
|
74
|
+
/** Test-only. */
|
|
75
|
+
export function __resetLaunchForTests(): void {
|
|
76
|
+
_armedAt = null;
|
|
77
|
+
_marks = [];
|
|
78
|
+
_completed = false;
|
|
79
|
+
}
|
package/src/mobile-vitals.ts
CHANGED
|
@@ -6,17 +6,27 @@
|
|
|
6
6
|
// init() calls checkColdStart() once. > 3 s ⇒ one `warn` event,
|
|
7
7
|
// scenario `slow_cold_start`, with the measured ms. Always pushes a
|
|
8
8
|
// `lifecycle` signal so even a fast start shows on the timeline.
|
|
9
|
+
//
|
|
10
|
+
// Pre-warmed processes (iOS ActivePrewarm; Android background start
|
|
11
|
+
// — FCM data message, JobScheduler — with the first Activity
|
|
12
|
+
// arriving much later) produce phantom samples: the measured span
|
|
13
|
+
// is mostly idle background time, not launch. Those samples still
|
|
14
|
+
// ship, flagged `prewarmed: true`, so the field data stays honest
|
|
15
|
+
// and slicable — but they never fire the slow-start warn (insight
|
|
16
|
+
// QIP-8 #1; the Firebase Performance bug class).
|
|
17
|
+
//
|
|
9
18
|
// Graceful no-op when the native module isn't linked (Expo Go,
|
|
10
19
|
// tests).
|
|
11
20
|
|
|
12
21
|
import { pushSignal } from '@goliapkg/sentori-core';
|
|
13
22
|
|
|
14
|
-
import { getNativeColdStartMs } from './native';
|
|
23
|
+
import { getNativeColdStartMs, getNativeColdStartPrewarmed } from './native';
|
|
15
24
|
import { warnDetected } from './verbs';
|
|
16
25
|
|
|
17
26
|
const SLOW_COLD_START_MS = 3_000;
|
|
18
27
|
|
|
19
28
|
let _coldStartMs: null | number = null;
|
|
29
|
+
let _coldStartPrewarmed = false;
|
|
20
30
|
let _coldStartCaptured = false;
|
|
21
31
|
|
|
22
32
|
/** Read the native-side cold start measurement once. Cached. */
|
|
@@ -25,18 +35,30 @@ export function getColdStartMs(): null | number {
|
|
|
25
35
|
_coldStartCaptured = true;
|
|
26
36
|
try {
|
|
27
37
|
_coldStartMs = getNativeColdStartMs();
|
|
38
|
+
_coldStartPrewarmed = getNativeColdStartPrewarmed();
|
|
28
39
|
} catch {
|
|
29
40
|
_coldStartMs = null;
|
|
30
41
|
}
|
|
31
42
|
return _coldStartMs;
|
|
32
43
|
}
|
|
33
44
|
|
|
34
|
-
/**
|
|
45
|
+
/** Whether the cached measurement is a pre-warmed phantom. Only
|
|
46
|
+
* meaningful after getColdStartMs() ran. */
|
|
47
|
+
export function isColdStartPrewarmed(): boolean {
|
|
48
|
+
return _coldStartPrewarmed;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Called once from init(): signal always, warn when slow AND real. */
|
|
35
52
|
export function checkColdStart(detectEnabled: boolean): void {
|
|
36
53
|
const ms = getColdStartMs();
|
|
37
54
|
if (ms === null) return;
|
|
38
|
-
|
|
39
|
-
|
|
55
|
+
const prewarmed = isColdStartPrewarmed();
|
|
56
|
+
pushSignal('lifecycle', {
|
|
57
|
+
phase: 'cold_start',
|
|
58
|
+
ms,
|
|
59
|
+
...(prewarmed ? { prewarmed: true } : {}),
|
|
60
|
+
});
|
|
61
|
+
if (detectEnabled && !prewarmed && ms > SLOW_COLD_START_MS) {
|
|
40
62
|
warnDetected('slow_cold_start', {}, { coldStartMs: ms, thresholdMs: SLOW_COLD_START_MS });
|
|
41
63
|
}
|
|
42
64
|
}
|
|
@@ -45,4 +67,5 @@ export function checkColdStart(detectEnabled: boolean): void {
|
|
|
45
67
|
export function __resetMobileVitalsForTests(): void {
|
|
46
68
|
_coldStartCaptured = false;
|
|
47
69
|
_coldStartMs = null;
|
|
70
|
+
_coldStartPrewarmed = false;
|
|
48
71
|
}
|
package/src/native.ts
CHANGED
|
@@ -22,6 +22,7 @@ type SentoriNativeModule = {
|
|
|
22
22
|
* Returns null when native side hasn't captured yet.
|
|
23
23
|
*/
|
|
24
24
|
getColdStartMs?: () => null | number
|
|
25
|
+
isColdStartPrewarmed?: () => boolean
|
|
25
26
|
/**
|
|
26
27
|
* v0.9.4 #1 — call once at JS init() to finalize the cold-start
|
|
27
28
|
* measurement. iOS subtracts from the app-delegate anchor;
|
|
@@ -349,6 +350,17 @@ export function getNativeColdStartMs(): null | number {
|
|
|
349
350
|
}
|
|
350
351
|
}
|
|
351
352
|
|
|
353
|
+
/** v5.6 — phantom-sample discriminator: the process was pre-warmed
|
|
354
|
+
* (iOS ActivePrewarm; Android background start with the first
|
|
355
|
+
* Activity arriving much later). Older binaries: false. */
|
|
356
|
+
export function getNativeColdStartPrewarmed(): boolean {
|
|
357
|
+
try {
|
|
358
|
+
return native()?.isColdStartPrewarmed?.() === true
|
|
359
|
+
} catch {
|
|
360
|
+
return false
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
352
364
|
/** v0.9.4 #1 — read slow/frozen frame counters since last reset. */
|
|
353
365
|
export function getNativeFrameCounters(): null | { frozen: number; slow: number } {
|
|
354
366
|
try {
|
package/src/transport.ts
CHANGED
|
@@ -20,7 +20,7 @@ const STORAGE_KEY = '@sentori/pending';
|
|
|
20
20
|
const MAX_PERSISTED = 1000;
|
|
21
21
|
|
|
22
22
|
// Pinned to package.json by a test — bump both together.
|
|
23
|
-
const SDK_VERSION = '5.
|
|
23
|
+
const SDK_VERSION = '5.6.0';
|
|
24
24
|
|
|
25
25
|
let _queue: WireEvent[] = [];
|
|
26
26
|
let _assertStats = new Map<string, AssertStat>();
|