@goliapkg/sentori-react-native 1.0.0-rc.8 → 1.0.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/bin/sentori-rn-upload-source-bundle.cjs +193 -0
- package/lib/capture.d.ts.map +1 -1
- package/lib/capture.js +9 -0
- package/lib/capture.js.map +1 -1
- package/lib/heartbeat.d.ts +9 -0
- package/lib/heartbeat.d.ts.map +1 -0
- package/lib/heartbeat.js +140 -0
- package/lib/heartbeat.js.map +1 -0
- package/lib/index.d.ts +15 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +15 -0
- package/lib/index.js.map +1 -1
- package/lib/init.d.ts +6 -0
- package/lib/init.d.ts.map +1 -1
- package/lib/init.js +18 -0
- package/lib/init.js.map +1 -1
- package/lib/install-id.d.ts +17 -0
- package/lib/install-id.d.ts.map +1 -0
- package/lib/install-id.js +125 -0
- package/lib/install-id.js.map +1 -0
- package/lib/navigation.d.ts +1 -0
- package/lib/navigation.d.ts.map +1 -1
- package/lib/navigation.js +20 -0
- package/lib/navigation.js.map +1 -1
- package/lib/replay.d.ts +27 -9
- package/lib/replay.d.ts.map +1 -1
- package/lib/replay.js +209 -167
- package/lib/replay.js.map +1 -1
- package/lib/report-security.d.ts +40 -0
- package/lib/report-security.d.ts.map +1 -0
- package/lib/report-security.js +159 -0
- package/lib/report-security.js.map +1 -0
- package/lib/track.d.ts +34 -0
- package/lib/track.d.ts.map +1 -0
- package/lib/track.js +98 -0
- package/lib/track.js.map +1 -0
- package/lib/transport.d.ts +15 -0
- package/lib/transport.d.ts.map +1 -1
- package/lib/transport.js +23 -0
- package/lib/transport.js.map +1 -1
- package/lib/trust-score.d.ts +20 -0
- package/lib/trust-score.d.ts.map +1 -0
- package/lib/trust-score.js +151 -0
- package/lib/trust-score.js.map +1 -0
- package/package.json +6 -2
- package/src/__tests__/install-id.test.ts +60 -0
- package/src/__tests__/replay-encoding.test.ts +237 -0
- package/src/__tests__/report-security.test.ts +106 -0
- package/src/__tests__/track.test.ts +91 -0
- package/src/capture.ts +8 -0
- package/src/heartbeat.ts +158 -0
- package/src/index.ts +24 -0
- package/src/init.ts +23 -0
- package/src/install-id.ts +146 -0
- package/src/navigation.ts +26 -0
- package/src/replay.ts +258 -176
- package/src/report-security.ts +165 -0
- package/src/track.ts +114 -0
- package/src/transport.ts +35 -0
- package/src/trust-score.ts +176 -0
package/lib/replay.js
CHANGED
|
@@ -1,70 +1,64 @@
|
|
|
1
|
-
//
|
|
1
|
+
// rc.9 — wireframe Session Replay v2 encoding.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
3
|
+
// Replaces rc.8's "one full snapshot per tick" with keyframe + delta:
|
|
4
|
+
// - Native walker still emits a full snapshot string every tick.
|
|
5
|
+
// - JS parses the snapshot, builds a fingerprint→node map, and either
|
|
6
|
+
// emits a keyframe (cold start / every KEYFRAME_INTERVAL_MS / when
|
|
7
|
+
// the delta would be bigger than a fresh key) OR a delta against
|
|
8
|
+
// the previous emit's reconstructed state.
|
|
9
|
+
// - Static-UI ticks produce zero-delta heartbeats which we drop.
|
|
8
10
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// • Replay fidelity: less faithful to pixels but enough to see
|
|
16
|
-
// which screen the user was on and what was on it. Dashboard
|
|
17
|
-
// player renders SVG rects — denser-looking than a 1 Hz
|
|
18
|
-
// screenshot strip.
|
|
11
|
+
// At 4 Hz capture / 4 s keyframes the wire bytes drop ~50 % vs rc.8 at
|
|
12
|
+
// 1 Hz for the same 60 s pre-error window, and the dashboard player
|
|
13
|
+
// cross-fades between captures at 24 fps so playback reads as motion
|
|
14
|
+
// rather than a 1 Hz slideshow.
|
|
15
|
+
//
|
|
16
|
+
// Wire schema: docs/replay-encoding-v2.md.
|
|
19
17
|
import { startSpan } from '@goliapkg/sentori-core';
|
|
20
18
|
import { getRegisteredMaskQuery } from './mask';
|
|
21
19
|
import { describeWireframeNative } from './native';
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
const
|
|
20
|
+
/** Default capture interval (2 Hz). Override via `replay.hz`. rc.10
|
|
21
|
+
* rolled this back from rc.9's 4 Hz default: iOS sim measured 1 ms
|
|
22
|
+
* per tick on a thin dev panel but extrapolation to a 200-node
|
|
23
|
+
* Insight-class UI on Android pushes JS-thread occupancy past 1 %,
|
|
24
|
+
* which violates the "几乎不能造成性能抖动" rule. Apps that want
|
|
25
|
+
* smoother playback motion can opt into `replay.hz: 4` explicitly. */
|
|
26
|
+
const TICK_INTERVAL_MS = 500;
|
|
27
|
+
/** How often to emit a fresh keyframe — caps reconstruction chain
|
|
28
|
+
* length and lets the player re-sync after a dropped line. */
|
|
29
|
+
const KEYFRAME_INTERVAL_MS = 4_000;
|
|
30
|
+
/** When the delta against the previous frame would carry ≥ this
|
|
31
|
+
* fraction of the current node count, prefer a fresh keyframe —
|
|
32
|
+
* emits roughly the same bytes but doesn't grow the chain. */
|
|
33
|
+
const DELTA_TO_KEYFRAME_RATIO = 0.4;
|
|
34
|
+
/** Floor under which the keyframe-vs-delta ratio heuristic does
|
|
35
|
+
* not apply. Trivial UIs (≤ 10 nodes — boot splash, dev panel,
|
|
36
|
+
* tests) shouldn't drop to keyframes on every change. */
|
|
37
|
+
const KEYFRAME_RATIO_MIN_NODES = 10;
|
|
38
|
+
/** Replay window kept in the ring buffer. captureException drains. */
|
|
39
|
+
const REPLAY_WINDOW_MS = 60_000;
|
|
40
|
+
/** Hard ceiling on ring item count — defence against a wedged tick
|
|
41
|
+
* clock filling memory; under normal capture rates we evict by time
|
|
42
|
+
* long before this fires. */
|
|
43
|
+
const MAX_RING_ITEMS = 1000;
|
|
44
|
+
/** Floor on tick period. < 100 ms the native view-tree walk dominates
|
|
45
|
+
* the JS thread on mid-tier Android. */
|
|
46
|
+
const MIN_TICK_PERIOD_MS = 100;
|
|
29
47
|
let _ring = [];
|
|
30
48
|
let _timer = null;
|
|
31
49
|
let _running = false;
|
|
32
|
-
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
* idle phase no longer evicts a useful pre-error frame.
|
|
38
|
-
*
|
|
39
|
-
* We only check against the most-recently-pushed snapshot, not the
|
|
40
|
-
* whole ring — that's cheap (one string comparison per tick) and
|
|
41
|
-
* catches the dominant case (idle screens). True content changes
|
|
42
|
-
* fall through and push as before.
|
|
43
|
-
*
|
|
44
|
-
* Budget verification on the iOS showcase (apps/ios-showcase): 60
|
|
45
|
-
* frames at ~120 bytes each → ≈ 7 KB raw NDJSON, well under the
|
|
46
|
-
* 500 KB attachment cap. Heavier RN apps with 200+ visible nodes
|
|
47
|
-
* per frame can land in the 400 KB band; future work in v1.x adds
|
|
48
|
-
* native gzip on upload if real-world traffic ever pushes the cap.
|
|
49
|
-
*/
|
|
50
|
-
let _lastPushed = null;
|
|
51
|
-
/** Native module ref, resolved once on first start. Caching here
|
|
52
|
-
* avoids the cost of `requireNativeModule('Sentori')` on every
|
|
53
|
-
* capture tick (Metro's require cache makes this cheap, but the
|
|
54
|
-
* per-tick string lookup and possible throw still cost more than
|
|
55
|
-
* reading a closed-over variable). */
|
|
50
|
+
/** Last emit's reconstructed state — fingerprint → node. Null until
|
|
51
|
+
* the first keyframe lands; reset on drain so the next session
|
|
52
|
+
* starts with a fresh keyframe. */
|
|
53
|
+
let _lastFrameState = null;
|
|
54
|
+
let _lastKeyframeTs = 0;
|
|
56
55
|
let _nativeMod = null;
|
|
56
|
+
let _keyframeIntervalMs = KEYFRAME_INTERVAL_MS;
|
|
57
57
|
export function startReplay(opts) {
|
|
58
58
|
if (_running)
|
|
59
59
|
return;
|
|
60
60
|
if (opts.mode !== 'wireframe')
|
|
61
61
|
return;
|
|
62
|
-
// v0.9.10 — gate via expo-modules-core's registry (same path the
|
|
63
|
-
// screenshot capture uses). The previous `isNativeModuleLinked`
|
|
64
|
-
// check looked at the legacy `RN.NativeModules` map, but the
|
|
65
|
-
// Sentori module is registered through expo-modules-core; the
|
|
66
|
-
// legacy map never sees it, so this branch returned "not linked"
|
|
67
|
-
// forever even with the pod correctly attached (Insight 2026-05-17).
|
|
68
62
|
const info = describeWireframeNative();
|
|
69
63
|
if (!info.bound) {
|
|
70
64
|
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
@@ -79,23 +73,15 @@ export function startReplay(opts) {
|
|
|
79
73
|
}
|
|
80
74
|
_running = true;
|
|
81
75
|
_nativeMod = loadNativeReplay();
|
|
82
|
-
|
|
76
|
+
_keyframeIntervalMs = opts.keyframeMs ?? KEYFRAME_INTERVAL_MS;
|
|
77
|
+
const hz = opts.hz ?? 2;
|
|
78
|
+
const period = Math.max(MIN_TICK_PERIOD_MS, Math.round(1000 / hz));
|
|
83
79
|
_timer = setInterval(() => {
|
|
84
80
|
captureTick();
|
|
85
81
|
}, period);
|
|
86
|
-
// v0.9.12 — Insight 2026-05-17 report: 0.9.11 emitted the
|
|
87
|
-
// "starting bound=true" line then went silent. Root cause was the
|
|
88
|
-
// `.unref?.()` call that used to live here. Hermes 0.81 doesn't
|
|
89
|
-
// ship a Timer object with the Node-style `unref` method, and the
|
|
90
|
-
// optional-chained call ended up dereferencing a `prototype`
|
|
91
|
-
// property on `undefined` — throwing synchronously inside
|
|
92
|
-
// startReplay, which RN's bridge swallowed silently. Net effect:
|
|
93
|
-
// setInterval registered, captureTick never invoked. Drop the
|
|
94
|
-
// call; replay tick lifecycle is bound to the app process, no
|
|
95
|
-
// event-loop tweak needed.
|
|
96
82
|
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
97
83
|
// eslint-disable-next-line no-console
|
|
98
|
-
console.warn('[sentori] replay: scheduled tick period=', period, 'ms');
|
|
84
|
+
console.warn('[sentori] replay: scheduled tick period=', period, 'ms keyframe=', _keyframeIntervalMs, 'ms');
|
|
99
85
|
}
|
|
100
86
|
}
|
|
101
87
|
export function stopReplay() {
|
|
@@ -108,6 +94,9 @@ export function stopReplay() {
|
|
|
108
94
|
_emptyTickCount = 0;
|
|
109
95
|
_emptyTickLogStride = 1;
|
|
110
96
|
_firstTickLogged = false;
|
|
97
|
+
_okTickCount = 0;
|
|
98
|
+
_thinTickCount = 0;
|
|
99
|
+
_thinTickLogStride = 1;
|
|
111
100
|
}
|
|
112
101
|
let _emptyTickCount = 0;
|
|
113
102
|
let _emptyTickLogStride = 1;
|
|
@@ -115,30 +104,15 @@ let _thinTickCount = 0;
|
|
|
115
104
|
let _thinTickLogStride = 1;
|
|
116
105
|
let _okTickCount = 0;
|
|
117
106
|
let _firstTickLogged = false;
|
|
118
|
-
/** Anything below this many nodes is suspicious — likely the
|
|
119
|
-
* walker bailed early (zero-size parent, masked root, etc.).
|
|
120
|
-
* Insight 2026-05-18 verify event saw 800-node payloads on some
|
|
121
|
-
* ticks and 1-3-node payloads on others; this threshold flags
|
|
122
|
-
* the latter without spamming on small-but-valid screens. */
|
|
123
107
|
const THIN_RESULT_NODES = 6;
|
|
124
108
|
function captureTick() {
|
|
125
109
|
if (!_running)
|
|
126
110
|
return;
|
|
127
|
-
// v0.9.12 — UNCONDITIONAL first-tick log. Proves the setInterval
|
|
128
|
-
// callback is firing at all, before any other code that could
|
|
129
|
-
// throw. 0.9.11's diagnostic was inside a `else if (snapshot==null)`
|
|
130
|
-
// branch that could only surface AFTER the native call returned;
|
|
131
|
-
// useless when the bug is that the tick body never enters.
|
|
132
111
|
if (typeof __DEV__ !== 'undefined' && __DEV__ && !_firstTickLogged) {
|
|
133
112
|
// eslint-disable-next-line no-console
|
|
134
113
|
console.warn('[sentori] replay tick: FIRST INVOCATION');
|
|
135
114
|
_firstTickLogged = true;
|
|
136
115
|
}
|
|
137
|
-
// 0.9.11 called startSpan OUTSIDE the catch block. If
|
|
138
|
-
// `@goliapkg/sentori-core` failed to initialise (or startSpan
|
|
139
|
-
// threw for any other reason on the first tick) the whole tick
|
|
140
|
-
// callback died silently. Wrap so worst case is "no span for this
|
|
141
|
-
// tick" not "no ticks for the session".
|
|
142
116
|
let tickSpan = null;
|
|
143
117
|
try {
|
|
144
118
|
tickSpan = startSpan('sentori.replay.tick', { name: 'tick' });
|
|
@@ -148,64 +122,29 @@ function captureTick() {
|
|
|
148
122
|
}
|
|
149
123
|
try {
|
|
150
124
|
const maskIds = readMaskIds();
|
|
151
|
-
const
|
|
152
|
-
if (typeof
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
_emptyTickCount = 0;
|
|
163
|
-
_emptyTickLogStride = 1;
|
|
164
|
-
// v1.0.0-rc.3 — Insight 2026-05-18 report: some ticks land
|
|
165
|
-
// valid non-empty JSON but with only the root View + 1-2 wrappers
|
|
166
|
-
// (the Android zero-size-bails-subtree bug, now fixed natively;
|
|
167
|
-
// this log catches similar regressions). Cheap node-count parse
|
|
168
|
-
// — we only look at one digit-level character class.
|
|
169
|
-
_okTickCount += 1;
|
|
170
|
-
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
171
|
-
const nodeCount = countNodesQuick(snapshot);
|
|
172
|
-
const sizeBytes = snapshot.length;
|
|
173
|
-
const isThin = nodeCount < THIN_RESULT_NODES;
|
|
174
|
-
if (isThin) {
|
|
175
|
-
_thinTickCount += 1;
|
|
176
|
-
if (_thinTickCount === 1 || _thinTickCount === _thinTickLogStride) {
|
|
177
|
-
// eslint-disable-next-line no-console
|
|
178
|
-
console.warn(`[sentori] replay tick: thin result nodes=${nodeCount} sizeBytes=${sizeBytes} (thin ticks so far: ${_thinTickCount})`);
|
|
179
|
-
_thinTickLogStride = Math.max(_thinTickLogStride * 10, 10);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
else {
|
|
183
|
-
_thinTickCount = 0;
|
|
184
|
-
_thinTickLogStride = 1;
|
|
185
|
-
}
|
|
186
|
-
// First good tick logs the shape so devs see it once.
|
|
187
|
-
if (_okTickCount === 1) {
|
|
188
|
-
// eslint-disable-next-line no-console
|
|
189
|
-
console.warn(`[sentori] replay tick: first ok — nodes=${nodeCount} sizeBytes=${sizeBytes}`);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
125
|
+
const snapshotJson = _nativeMod?.captureWireframe?.(maskIds);
|
|
126
|
+
if (typeof snapshotJson !== 'string' || snapshotJson.length === 0) {
|
|
127
|
+
handleEmptyTick(snapshotJson);
|
|
128
|
+
tickSpan?.finish({ status: 'ok' });
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
let snapshot;
|
|
132
|
+
try {
|
|
133
|
+
snapshot = JSON.parse(snapshotJson);
|
|
192
134
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
// of times but ring stays empty → native returned null/empty.
|
|
196
|
-
// Log on a back-off schedule (1st, 10th, 100th, …) so the
|
|
197
|
-
// diagnostic is visible without spamming Metro at 1 Hz for a
|
|
198
|
-
// 15-minute session.
|
|
199
|
-
_emptyTickCount += 1;
|
|
200
|
-
if (_emptyTickCount === 1 || _emptyTickCount === _emptyTickLogStride) {
|
|
135
|
+
catch (e) {
|
|
136
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
201
137
|
// eslint-disable-next-line no-console
|
|
202
|
-
console.warn('[sentori] replay tick: native
|
|
203
|
-
? 'null'
|
|
204
|
-
: typeof snapshot === 'string'
|
|
205
|
-
? `empty (length=${snapshot.length})`
|
|
206
|
-
: typeof snapshot, `(empty ticks so far: ${_emptyTickCount})`);
|
|
207
|
-
_emptyTickLogStride = Math.max(_emptyTickLogStride * 10, 10);
|
|
138
|
+
console.warn('[sentori] replay tick: native JSON parse failed', e);
|
|
208
139
|
}
|
|
140
|
+
tickSpan?.finish({ status: 'error' });
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
_emptyTickCount = 0;
|
|
144
|
+
_emptyTickLogStride = 1;
|
|
145
|
+
encodeAndPush(snapshot);
|
|
146
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
147
|
+
diagnosticForTick(snapshot, snapshotJson.length);
|
|
209
148
|
}
|
|
210
149
|
tickSpan?.finish({ status: 'ok' });
|
|
211
150
|
}
|
|
@@ -219,28 +158,124 @@ function captureTick() {
|
|
|
219
158
|
}
|
|
220
159
|
}
|
|
221
160
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
161
|
+
function encodeAndPush(snapshot) {
|
|
162
|
+
const currentState = new Map();
|
|
163
|
+
for (const n of snapshot.nodes)
|
|
164
|
+
currentState.set(fingerprint(n), n);
|
|
165
|
+
const ts = snapshot.ts;
|
|
166
|
+
const isCold = _lastFrameState === null;
|
|
167
|
+
const keyframeOverdue = ts - _lastKeyframeTs >= _keyframeIntervalMs;
|
|
168
|
+
let line;
|
|
169
|
+
if (isCold || keyframeOverdue) {
|
|
170
|
+
line = encodeKeyframe(snapshot);
|
|
171
|
+
_lastKeyframeTs = ts;
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
const delta = computeDelta(_lastFrameState, currentState);
|
|
175
|
+
const totalChanged = delta.added.length + delta.changed.length + delta.removed.length;
|
|
176
|
+
if (totalChanged === 0) {
|
|
177
|
+
// No-op heartbeat — drop. Keep _lastFrameState as-is (identical).
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (currentState.size >= KEYFRAME_RATIO_MIN_NODES &&
|
|
181
|
+
totalChanged >= currentState.size * DELTA_TO_KEYFRAME_RATIO) {
|
|
182
|
+
// Big screen transition on a substantial UI — emit a fresh
|
|
183
|
+
// keyframe so reconstruction doesn't carry a near-rewrite delta.
|
|
184
|
+
line = encodeKeyframe(snapshot);
|
|
185
|
+
_lastKeyframeTs = ts;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
line = JSON.stringify({
|
|
189
|
+
ts,
|
|
190
|
+
kind: 'delta',
|
|
191
|
+
added: delta.added,
|
|
192
|
+
changed: delta.changed,
|
|
193
|
+
removed: delta.removed,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
_ring.push({ ts, line });
|
|
198
|
+
evictRing(ts);
|
|
199
|
+
_lastFrameState = currentState;
|
|
200
|
+
}
|
|
201
|
+
function encodeKeyframe(snapshot) {
|
|
202
|
+
return JSON.stringify({
|
|
203
|
+
ts: snapshot.ts,
|
|
204
|
+
kind: 'key',
|
|
205
|
+
width: snapshot.width,
|
|
206
|
+
height: snapshot.height,
|
|
207
|
+
nodes: snapshot.nodes,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
function evictRing(nowTs) {
|
|
211
|
+
const cutoff = nowTs - REPLAY_WINDOW_MS;
|
|
212
|
+
while (_ring.length > 0 && _ring[0].ts < cutoff)
|
|
213
|
+
_ring.shift();
|
|
214
|
+
while (_ring.length > MAX_RING_ITEMS)
|
|
215
|
+
_ring.shift();
|
|
216
|
+
}
|
|
217
|
+
/** Fingerprint integer-rounds before joining so sub-pixel jitter from
|
|
218
|
+
* RN's Fabric layout (occasionally floats) doesn't break stable
|
|
219
|
+
* matching across ticks. */
|
|
220
|
+
function fingerprint(n) {
|
|
221
|
+
return `${n.x | 0},${n.y | 0},${n.w | 0},${n.h | 0}`;
|
|
222
|
+
}
|
|
223
|
+
export function computeDelta(prev, curr) {
|
|
224
|
+
const added = [];
|
|
225
|
+
const changed = [];
|
|
226
|
+
const removed = [];
|
|
227
|
+
for (const [fp, node] of curr) {
|
|
228
|
+
const p = prev.get(fp);
|
|
229
|
+
if (!p) {
|
|
230
|
+
added.push(node);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if ((p.kind ?? '') !== (node.kind ?? '') ||
|
|
234
|
+
(p.color ?? '') !== (node.color ?? '') ||
|
|
235
|
+
(p.text ?? '') !== (node.text ?? '')) {
|
|
236
|
+
changed.push(node);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
for (const [fp, node] of prev) {
|
|
240
|
+
if (!curr.has(fp))
|
|
241
|
+
removed.push({ x: node.x, y: node.y, w: node.w, h: node.h });
|
|
242
|
+
}
|
|
243
|
+
return { added, changed, removed };
|
|
244
|
+
}
|
|
245
|
+
function handleEmptyTick(snapshot) {
|
|
246
|
+
if (typeof __DEV__ === 'undefined' || !__DEV__)
|
|
247
|
+
return;
|
|
248
|
+
_emptyTickCount += 1;
|
|
249
|
+
if (_emptyTickCount === 1 || _emptyTickCount === _emptyTickLogStride) {
|
|
250
|
+
// eslint-disable-next-line no-console
|
|
251
|
+
console.warn('[sentori] replay tick: native returned', snapshot === null
|
|
252
|
+
? 'null'
|
|
253
|
+
: typeof snapshot === 'string'
|
|
254
|
+
? `empty (length=${snapshot.length})`
|
|
255
|
+
: typeof snapshot, `(empty ticks so far: ${_emptyTickCount})`);
|
|
256
|
+
_emptyTickLogStride = Math.max(_emptyTickLogStride * 10, 10);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function diagnosticForTick(snapshot, snapshotBytes) {
|
|
260
|
+
_okTickCount += 1;
|
|
261
|
+
const nodeCount = snapshot.nodes.length;
|
|
262
|
+
const isThin = nodeCount < THIN_RESULT_NODES;
|
|
263
|
+
if (isThin) {
|
|
264
|
+
_thinTickCount += 1;
|
|
265
|
+
if (_thinTickCount === 1 || _thinTickCount === _thinTickLogStride) {
|
|
266
|
+
// eslint-disable-next-line no-console
|
|
267
|
+
console.warn(`[sentori] replay tick: thin result nodes=${nodeCount} sizeBytes=${snapshotBytes} (thin ticks so far: ${_thinTickCount})`);
|
|
268
|
+
_thinTickLogStride = Math.max(_thinTickLogStride * 10, 10);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
_thinTickCount = 0;
|
|
273
|
+
_thinTickLogStride = 1;
|
|
274
|
+
}
|
|
275
|
+
if (_okTickCount === 1) {
|
|
276
|
+
// eslint-disable-next-line no-console
|
|
277
|
+
console.warn(`[sentori] replay tick: first ok — nodes=${nodeCount} sizeBytes=${snapshotBytes}`);
|
|
242
278
|
}
|
|
243
|
-
return count;
|
|
244
279
|
}
|
|
245
280
|
function readMaskIds() {
|
|
246
281
|
const q = getRegisteredMaskQuery();
|
|
@@ -263,28 +298,35 @@ function loadNativeReplay() {
|
|
|
263
298
|
return null;
|
|
264
299
|
}
|
|
265
300
|
}
|
|
266
|
-
/** rc.4 — surface "is replay subsystem alive" so the captureException
|
|
267
|
-
* debug log can label `wantReplay` alongside `wantScreenshot` /
|
|
268
|
-
* `wantSessionTrail`. Insight 2026-05-18 verify flagged that the
|
|
269
|
-
* pre-rc.4 log line was missing `wantReplay`, leaving the failure
|
|
270
|
-
* shape ambiguous (config-off vs. ring-empty vs. attach-failed). */
|
|
271
301
|
export function isReplayRunning() {
|
|
272
302
|
return _running;
|
|
273
303
|
}
|
|
274
|
-
/** Drain the ring as NDJSON (
|
|
275
|
-
* when the ring is empty.
|
|
276
|
-
* replay starts fresh. */
|
|
304
|
+
/** Drain the ring as NDJSON (keyframe or delta per line). Empty
|
|
305
|
+
* string when the ring is empty. Resets state so the next session's
|
|
306
|
+
* replay starts with a fresh keyframe. */
|
|
277
307
|
export function drainReplay() {
|
|
278
308
|
if (_ring.length === 0)
|
|
279
309
|
return '';
|
|
280
|
-
const out = _ring.join('\n');
|
|
310
|
+
const out = _ring.map((r) => r.line).join('\n');
|
|
281
311
|
_ring = [];
|
|
282
|
-
|
|
312
|
+
_lastFrameState = null;
|
|
313
|
+
_lastKeyframeTs = 0;
|
|
283
314
|
return out;
|
|
284
315
|
}
|
|
285
316
|
export function __resetReplayForTests() {
|
|
286
317
|
stopReplay();
|
|
287
318
|
_ring = [];
|
|
288
|
-
|
|
319
|
+
_lastFrameState = null;
|
|
320
|
+
_lastKeyframeTs = 0;
|
|
321
|
+
}
|
|
322
|
+
/** rc.9 — test seam. Lets unit tests drive the encoder without a
|
|
323
|
+
* native module; pretends we received `frameJson` on the tick. */
|
|
324
|
+
export function __feedTickForTests(frameJson) {
|
|
325
|
+
if (!_running) {
|
|
326
|
+
// Simulate "running" without an actual setInterval — caller drives.
|
|
327
|
+
_running = true;
|
|
328
|
+
}
|
|
329
|
+
const snapshot = JSON.parse(frameJson);
|
|
330
|
+
encodeAndPush(snapshot);
|
|
289
331
|
}
|
|
290
332
|
//# sourceMappingURL=replay.js.map
|
package/lib/replay.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type SecurityReportData = Record<string, unknown>;
|
|
2
|
+
/**
|
|
3
|
+
* Report an arbitrary security signal. Fire-and-forget; resolves
|
|
4
|
+
* with the server-assigned event id on success or `null` on any
|
|
5
|
+
* failure (network down, server unhappy, SDK not initialised).
|
|
6
|
+
*
|
|
7
|
+
* Use the dedicated helpers (`reportPinMismatch`, future
|
|
8
|
+
* `reportRootDetected`, …) when their shape applies — the dashboard
|
|
9
|
+
* renders kind-specific panels off the well-known kinds.
|
|
10
|
+
*/
|
|
11
|
+
export declare function reportSecurity(kind: string, data?: SecurityReportData): Promise<null | string>;
|
|
12
|
+
/**
|
|
13
|
+
* v1.1 chunk S4 — link the current device's user / install to a
|
|
14
|
+
* federated identity (e.g. a Google sub or Apple `sub`). Idempotent;
|
|
15
|
+
* call on every sign-in. Posts to `/v1/security/link`. The dashboard
|
|
16
|
+
* uses this to stitch the same user across projects in the Posture
|
|
17
|
+
* cross-project view.
|
|
18
|
+
*
|
|
19
|
+
* Privacy: only the opaque OAuth `subject` value travels. Never pass
|
|
20
|
+
* the email, display name, avatar, or any other identity attribute.
|
|
21
|
+
*/
|
|
22
|
+
export declare function linkFederatedIdentity(args: {
|
|
23
|
+
provider: string;
|
|
24
|
+
subject: string;
|
|
25
|
+
userId?: string;
|
|
26
|
+
}): Promise<boolean>;
|
|
27
|
+
/**
|
|
28
|
+
* TLS certificate pin mismatch — caller observed a server cert that
|
|
29
|
+
* didn't match the configured pin set. Posts `kind = 'pin.mismatch'`
|
|
30
|
+
* with the expected + observed pin (or hash) so the dashboard's
|
|
31
|
+
* Pin anomaly panel can cluster reports by server.
|
|
32
|
+
*/
|
|
33
|
+
export declare function reportPinMismatch(args: {
|
|
34
|
+
expected: string;
|
|
35
|
+
observed: string;
|
|
36
|
+
/** Hostname the SDK was connecting to. Used by the dashboard to
|
|
37
|
+
* cluster reports per server. */
|
|
38
|
+
serverName: string;
|
|
39
|
+
}): Promise<null | string>;
|
|
40
|
+
//# sourceMappingURL=report-security.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-security.d.ts","sourceRoot":"","sources":["../src/report-security.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEzD;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAkCxB;AAED;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,OAAO,CAAC,CA2BnB;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB;sCACkC;IAClC,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAuCzB"}
|