@goliapkg/sentori-react-native 4.0.0 → 5.1.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/MIGRATION.md +131 -0
- package/README.md +40 -30
- package/android/build.gradle +21 -0
- package/android/src/main/java/com/sentori/SentoriCrashHandler.kt +29 -0
- package/android/src/main/java/com/sentori/SentoriModule.kt +8 -0
- package/android/src/main/java/com/sentori/SentoriScreenshotCapture.kt +36 -4
- package/android/src/test/java/com/sentori/SentoriCrashHandlerTest.kt +7 -2
- package/android/src/test/resources/robolectric.properties +10 -0
- package/ios/SentoriCrashHandler.swift +48 -4
- package/ios/SentoriModule.swift +12 -0
- package/ios/SentoriScreenshotCapture.swift +34 -3
- package/lib/config.d.ts +17 -47
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +3 -0
- package/lib/config.js.map +1 -1
- package/lib/device.d.ts +5 -0
- package/lib/device.d.ts.map +1 -0
- package/lib/device.js +29 -0
- package/lib/device.js.map +1 -0
- package/lib/emit-hooks.d.ts +7 -0
- package/lib/emit-hooks.d.ts.map +1 -0
- package/lib/emit-hooks.js +24 -0
- package/lib/emit-hooks.js.map +1 -0
- package/lib/error-boundary.d.ts.map +1 -1
- package/lib/error-boundary.js +1 -1
- package/lib/error-boundary.js.map +1 -1
- package/lib/feature-flags.d.ts.map +1 -1
- package/lib/handlers/dev-symbolicate.d.ts +1 -1
- package/lib/handlers/dev-symbolicate.d.ts.map +1 -1
- package/lib/handlers/dev-symbolicate.js +2 -0
- package/lib/handlers/dev-symbolicate.js.map +1 -1
- package/lib/handlers/global.js +1 -1
- package/lib/handlers/global.js.map +1 -1
- package/lib/handlers/lifecycle.js +3 -3
- package/lib/handlers/lifecycle.js.map +1 -1
- package/lib/handlers/network.d.ts +2 -2
- package/lib/handlers/network.d.ts.map +1 -1
- package/lib/handlers/network.js +69 -187
- package/lib/handlers/network.js.map +1 -1
- package/lib/handlers/promise.js +1 -1
- package/lib/handlers/promise.js.map +1 -1
- package/lib/handlers/screenshot.d.ts.map +1 -1
- package/lib/handlers/screenshot.js +3 -12
- package/lib/handlers/screenshot.js.map +1 -1
- package/lib/index.d.ts +32 -109
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +54 -103
- package/lib/index.js.map +1 -1
- package/lib/init.d.ts +3 -150
- package/lib/init.d.ts.map +1 -1
- package/lib/init.js +100 -261
- package/lib/init.js.map +1 -1
- package/lib/long-task-monitor.d.ts +1 -7
- package/lib/long-task-monitor.d.ts.map +1 -1
- package/lib/long-task-monitor.js +37 -58
- package/lib/long-task-monitor.js.map +1 -1
- package/lib/mask.d.ts +5 -13
- package/lib/mask.d.ts.map +1 -1
- package/lib/mask.js +29 -30
- package/lib/mask.js.map +1 -1
- package/lib/mobile-vitals.d.ts +3 -31
- package/lib/mobile-vitals.d.ts.map +1 -1
- package/lib/mobile-vitals.js +22 -61
- package/lib/mobile-vitals.js.map +1 -1
- package/lib/native-pending.d.ts +3 -0
- package/lib/native-pending.d.ts.map +1 -0
- package/lib/native-pending.js +64 -0
- package/lib/native-pending.js.map +1 -0
- package/lib/native.d.ts +4 -11
- package/lib/native.d.ts.map +1 -1
- package/lib/native.js +23 -0
- package/lib/native.js.map +1 -1
- package/lib/navigation.d.ts +3 -17
- package/lib/navigation.d.ts.map +1 -1
- package/lib/navigation.js +32 -133
- package/lib/navigation.js.map +1 -1
- package/lib/netinfo.d.ts +1 -1
- package/lib/netinfo.d.ts.map +1 -1
- package/lib/netinfo.js +2 -2
- package/lib/netinfo.js.map +1 -1
- package/lib/push.d.ts.map +1 -1
- package/lib/push.js +2 -4
- package/lib/push.js.map +1 -1
- package/lib/rage-tap.d.ts.map +1 -1
- package/lib/rage-tap.js +56 -23
- package/lib/rage-tap.js.map +1 -1
- package/lib/replay-screens.d.ts +15 -0
- package/lib/replay-screens.d.ts.map +1 -0
- package/lib/replay-screens.js +87 -0
- package/lib/replay-screens.js.map +1 -0
- package/lib/replay.d.ts.map +1 -1
- package/lib/replay.js +3 -24
- package/lib/replay.js.map +1 -1
- package/lib/responsiveness-detector.d.ts +18 -0
- package/lib/responsiveness-detector.d.ts.map +1 -0
- package/lib/responsiveness-detector.js +55 -0
- package/lib/responsiveness-detector.js.map +1 -0
- package/lib/scope.d.ts +7 -0
- package/lib/scope.d.ts.map +1 -0
- package/lib/scope.js +41 -0
- package/lib/scope.js.map +1 -0
- package/lib/transport.d.ts +19 -92
- package/lib/transport.d.ts.map +1 -1
- package/lib/transport.js +75 -288
- package/lib/transport.js.map +1 -1
- package/lib/verbs.d.ts +16 -0
- package/lib/verbs.d.ts.map +1 -0
- package/lib/verbs.js +153 -0
- package/lib/verbs.js.map +1 -0
- package/package.json +4 -3
- package/src/__tests__/iron-rule.test.ts +128 -0
- package/src/__tests__/replay-screens.test.ts +63 -0
- package/src/__tests__/responsiveness-detector.test.ts +74 -0
- package/src/__tests__/screenshot.test.ts +0 -2
- package/src/__tests__/transport.test.ts +66 -213
- package/src/__tests__/verbs.test.ts +91 -0
- package/src/config.ts +20 -47
- package/src/device.ts +36 -0
- package/src/emit-hooks.ts +30 -0
- package/src/error-boundary.tsx +1 -1
- package/src/handlers/dev-symbolicate.ts +2 -1
- package/src/handlers/global.ts +1 -1
- package/src/handlers/lifecycle.ts +3 -3
- package/src/handlers/network.ts +95 -211
- package/src/handlers/promise.ts +1 -1
- package/src/handlers/screenshot.ts +3 -11
- package/src/index.ts +84 -215
- package/src/init.ts +115 -449
- package/src/long-task-monitor.ts +42 -64
- package/src/mask.ts +29 -31
- package/src/mobile-vitals.ts +22 -82
- package/src/native-pending.ts +92 -0
- package/src/native.ts +33 -0
- package/src/navigation.ts +34 -142
- package/src/netinfo.ts +3 -3
- package/src/push.ts +2 -5
- package/src/rage-tap.tsx +72 -25
- package/src/replay-screens.ts +95 -0
- package/src/replay.ts +3 -20
- package/src/responsiveness-detector.ts +66 -0
- package/src/scope.ts +50 -0
- package/src/transport.ts +84 -371
- package/src/verbs.ts +198 -0
- package/lib/breadcrumbs.d.ts +0 -18
- package/lib/breadcrumbs.d.ts.map +0 -1
- package/lib/breadcrumbs.js +0 -42
- package/lib/breadcrumbs.js.map +0 -1
- package/lib/bundle-info.d.ts +0 -12
- package/lib/bundle-info.d.ts.map +0 -1
- package/lib/bundle-info.js +0 -73
- package/lib/bundle-info.js.map +0 -1
- package/lib/capture.d.ts +0 -140
- package/lib/capture.d.ts.map +0 -1
- package/lib/capture.js +0 -511
- package/lib/capture.js.map +0 -1
- package/lib/compat/sentry.d.ts +0 -126
- package/lib/compat/sentry.d.ts.map +0 -1
- package/lib/compat/sentry.js +0 -332
- package/lib/compat/sentry.js.map +0 -1
- package/lib/control-channel.d.ts +0 -6
- package/lib/control-channel.d.ts.map +0 -1
- package/lib/control-channel.js +0 -96
- package/lib/control-channel.js.map +0 -1
- package/lib/expo-compat.d.ts +0 -270
- package/lib/expo-compat.d.ts.map +0 -1
- package/lib/expo-compat.js +0 -500
- package/lib/expo-compat.js.map +0 -1
- package/lib/feedback-widget.d.ts +0 -35
- package/lib/feedback-widget.d.ts.map +0 -1
- package/lib/feedback-widget.js +0 -193
- package/lib/feedback-widget.js.map +0 -1
- package/lib/feedback.d.ts +0 -2
- package/lib/feedback.d.ts.map +0 -1
- package/lib/feedback.js +0 -17
- package/lib/feedback.js.map +0 -1
- package/lib/heartbeat.d.ts +0 -9
- package/lib/heartbeat.d.ts.map +0 -1
- package/lib/heartbeat.js +0 -138
- package/lib/heartbeat.js.map +0 -1
- package/lib/index.js.bak +0 -64
- package/lib/install-id.d.ts +0 -17
- package/lib/install-id.d.ts.map +0 -1
- package/lib/install-id.js +0 -125
- package/lib/install-id.js.map +0 -1
- package/lib/lifecycle.d.ts +0 -28
- package/lib/lifecycle.d.ts.map +0 -1
- package/lib/lifecycle.js +0 -73
- package/lib/lifecycle.js.map +0 -1
- package/lib/measure.d.ts +0 -4
- package/lib/measure.d.ts.map +0 -1
- package/lib/measure.js +0 -25
- package/lib/measure.js.map +0 -1
- package/lib/metrics.d.ts +0 -24
- package/lib/metrics.d.ts.map +0 -1
- package/lib/metrics.js +0 -81
- package/lib/metrics.js.map +0 -1
- package/lib/pre-crash-sentinel.d.ts +0 -12
- package/lib/pre-crash-sentinel.d.ts.map +0 -1
- package/lib/pre-crash-sentinel.js +0 -116
- package/lib/pre-crash-sentinel.js.map +0 -1
- package/lib/report-security.d.ts +0 -40
- package/lib/report-security.d.ts.map +0 -1
- package/lib/report-security.js +0 -159
- package/lib/report-security.js.map +0 -1
- package/lib/runtime-metrics-fps.d.ts +0 -19
- package/lib/runtime-metrics-fps.d.ts.map +0 -1
- package/lib/runtime-metrics-fps.js +0 -128
- package/lib/runtime-metrics-fps.js.map +0 -1
- package/lib/runtime-metrics-heap.d.ts +0 -13
- package/lib/runtime-metrics-heap.d.ts.map +0 -1
- package/lib/runtime-metrics-heap.js +0 -67
- package/lib/runtime-metrics-heap.js.map +0 -1
- package/lib/runtime-metrics-network.d.ts +0 -23
- package/lib/runtime-metrics-network.d.ts.map +0 -1
- package/lib/runtime-metrics-network.js +0 -99
- package/lib/runtime-metrics-network.js.map +0 -1
- package/lib/runtime-metrics.d.ts +0 -43
- package/lib/runtime-metrics.d.ts.map +0 -1
- package/lib/runtime-metrics.js +0 -115
- package/lib/runtime-metrics.js.map +0 -1
- package/lib/sample-profiler.d.ts +0 -21
- package/lib/sample-profiler.d.ts.map +0 -1
- package/lib/sample-profiler.js +0 -152
- package/lib/sample-profiler.js.map +0 -1
- package/lib/sentinel-context.d.ts +0 -4
- package/lib/sentinel-context.d.ts.map +0 -1
- package/lib/sentinel-context.js +0 -52
- package/lib/sentinel-context.js.map +0 -1
- package/lib/session-tracker.d.ts +0 -6
- package/lib/session-tracker.d.ts.map +0 -1
- package/lib/session-tracker.js +0 -50
- package/lib/session-tracker.js.map +0 -1
- package/lib/stack.d.ts +0 -3
- package/lib/stack.d.ts.map +0 -1
- package/lib/stack.js +0 -7
- package/lib/stack.js.map +0 -1
- package/lib/state-snapshots.d.ts +0 -33
- package/lib/state-snapshots.d.ts.map +0 -1
- package/lib/state-snapshots.js +0 -109
- package/lib/state-snapshots.js.map +0 -1
- package/lib/track.d.ts +0 -34
- package/lib/track.d.ts.map +0 -1
- package/lib/track.js +0 -107
- package/lib/track.js.map +0 -1
- package/lib/trail.d.ts +0 -12
- package/lib/trail.d.ts.map +0 -1
- package/lib/trail.js +0 -28
- package/lib/trail.js.map +0 -1
- package/lib/trust-score.d.ts +0 -20
- package/lib/trust-score.d.ts.map +0 -1
- package/lib/trust-score.js +0 -151
- package/lib/trust-score.js.map +0 -1
- package/lib/types.d.ts +0 -2
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js +0 -4
- package/lib/types.js.map +0 -1
- package/lib/uuid.d.ts +0 -2
- package/lib/uuid.d.ts.map +0 -1
- package/lib/uuid.js +0 -3
- package/lib/uuid.js.map +0 -1
- package/src/__tests__/before-send.test.ts +0 -72
- package/src/__tests__/breadcrumbs.test.ts +0 -44
- package/src/__tests__/capture-replay.test.ts +0 -150
- package/src/__tests__/compat-sentry.test.ts +0 -121
- package/src/__tests__/control-channel.test.ts +0 -111
- package/src/__tests__/install-id.test.ts +0 -60
- package/src/__tests__/long-task-monitor.test.ts +0 -59
- package/src/__tests__/measure.test.ts +0 -45
- package/src/__tests__/navigation.test.ts +0 -175
- package/src/__tests__/push.test.ts +0 -238
- package/src/__tests__/report-security.test.ts +0 -106
- package/src/__tests__/runtime-metrics-instruments.test.ts +0 -109
- package/src/__tests__/runtime-metrics-network.test.ts +0 -78
- package/src/__tests__/sample-profiler.test.ts +0 -59
- package/src/__tests__/stack.test.ts +0 -45
- package/src/__tests__/state-snapshots.test.ts +0 -119
- package/src/__tests__/tracing.test.ts +0 -201
- package/src/__tests__/track.test.ts +0 -91
- package/src/__tests__/uuid.test.ts +0 -41
- package/src/breadcrumbs.ts +0 -57
- package/src/bundle-info.ts +0 -95
- package/src/capture.ts +0 -660
- package/src/compat/sentry.ts +0 -490
- package/src/control-channel.ts +0 -98
- package/src/expo-compat.ts +0 -698
- package/src/feedback-widget.tsx +0 -316
- package/src/feedback.ts +0 -21
- package/src/heartbeat.ts +0 -157
- package/src/install-id.ts +0 -146
- package/src/lifecycle.ts +0 -76
- package/src/measure.ts +0 -28
- package/src/metrics.ts +0 -94
- package/src/pre-crash-sentinel.ts +0 -140
- package/src/report-security.ts +0 -165
- package/src/runtime-metrics-fps.ts +0 -137
- package/src/runtime-metrics-heap.ts +0 -78
- package/src/runtime-metrics-network.ts +0 -103
- package/src/runtime-metrics.ts +0 -122
- package/src/sample-profiler.ts +0 -169
- package/src/sentinel-context.ts +0 -60
- package/src/session-tracker.ts +0 -53
- package/src/stack.ts +0 -10
- package/src/state-snapshots.ts +0 -133
- package/src/track.ts +0 -123
- package/src/trail.ts +0 -32
- package/src/trust-score.ts +0 -176
- package/src/types.ts +0 -21
- package/src/uuid.ts +0 -2
package/lib/trail.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// Phase 46 — singleton TrailBuffer for the RN SDK.
|
|
2
|
-
//
|
|
3
|
-
// Kept in its own module so callers (including navigation.ts, which
|
|
4
|
-
// is intentionally lightweight) can record steps without pulling in
|
|
5
|
-
// capture.ts → handlers/screenshot.ts → react-native. The buffer is
|
|
6
|
-
// drained inside capture.ts when an event captures and
|
|
7
|
-
// `sessionTrailEnabled` is on.
|
|
8
|
-
import { TrailBuffer } from '@goliapkg/sentori-core';
|
|
9
|
-
const _trail = new TrailBuffer(30);
|
|
10
|
-
/**
|
|
11
|
-
* Phase 46 — record a step into the session-trail buffer. The buffer
|
|
12
|
-
* is a fixed-size FIFO (default 30 steps); pushing past capacity
|
|
13
|
-
* drops the oldest entry. Steps are only uploaded if
|
|
14
|
-
* `init({ capture: { sessionTrail: true } })` is on AND a
|
|
15
|
-
* `captureException` follows.
|
|
16
|
-
*/
|
|
17
|
-
export const captureStep = (label, opts) => {
|
|
18
|
-
_trail.push({
|
|
19
|
-
ts: Date.now(),
|
|
20
|
-
label,
|
|
21
|
-
...(opts ?? {}),
|
|
22
|
-
});
|
|
23
|
-
};
|
|
24
|
-
export const getTrailBuffer = () => _trail;
|
|
25
|
-
export const __resetTrailForTests = () => {
|
|
26
|
-
_trail.clear();
|
|
27
|
-
};
|
|
28
|
-
//# sourceMappingURL=trail.js.map
|
package/lib/trail.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"trail.js","sourceRoot":"","sources":["../src/trail.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,EAAE;AACF,oEAAoE;AACpE,oEAAoE;AACpE,oEAAoE;AACpE,uDAAuD;AACvD,+BAA+B;AAE/B,OAAO,EAAE,WAAW,EAAkB,MAAM,wBAAwB,CAAC;AAErE,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,EAAE,CAAC,CAAC;AAEnC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAa,EAAE,IAAyB,EAAQ,EAAE;IAC5E,MAAM,CAAC,IAAI,CAAC;QACV,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE;QACd,KAAK;QACL,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;KAChB,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,GAAgB,EAAE,CAAC,MAAM,CAAC;AAExD,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAS,EAAE;IAC7C,MAAM,CAAC,KAAK,EAAE,CAAC;AACjB,CAAC,CAAC"}
|
package/lib/trust-score.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export type TrustSignal = {
|
|
2
|
-
count: number;
|
|
3
|
-
kind: string;
|
|
4
|
-
weight: number;
|
|
5
|
-
};
|
|
6
|
-
export type TrustScore = {
|
|
7
|
-
computedAt: string;
|
|
8
|
-
installId: string;
|
|
9
|
-
score: number;
|
|
10
|
-
signals: TrustSignal[];
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* Resolve the device's trust score. Always resolves — never throws,
|
|
14
|
-
* never rejects. The two-layer cache means the first call after cold
|
|
15
|
-
* start usually serves a < 1 ms result while a background refresh
|
|
16
|
-
* keeps the value current.
|
|
17
|
-
*/
|
|
18
|
-
export declare function queryTrustScore(): Promise<TrustScore>;
|
|
19
|
-
export declare function __resetTrustScoreForTests(): void;
|
|
20
|
-
//# sourceMappingURL=trust-score.d.ts.map
|
package/lib/trust-score.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"trust-score.d.ts","sourceRoot":"","sources":["../src/trust-score.ts"],"names":[],"mappings":"AA6BA,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB,CAAC;AAqFF;;;;;GAKG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,UAAU,CAAC,CAuC3D;AAED,wBAAgB,yBAAyB,IAAI,IAAI,CAGhD"}
|
package/lib/trust-score.js
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
// v1.1 chunk S3 — `sentori.queryTrustScore()` for the host app.
|
|
2
|
-
//
|
|
3
|
-
// Returns the device's current trust score (0–100, higher = healthier)
|
|
4
|
-
// plus the per-kind signal mix that produced it. The host app uses
|
|
5
|
-
// this to decide whether to step up authentication, decline a
|
|
6
|
-
// purchase, or simply log a soft warning.
|
|
7
|
-
//
|
|
8
|
-
// Caching:
|
|
9
|
-
// L1 — process-memory, ~30 s. Same process should not re-fetch on
|
|
10
|
-
// every screen render; the score moves on a "minutes" cadence.
|
|
11
|
-
// L2 — install-id-derived AsyncStorage entry, ~5 minutes. Lets the
|
|
12
|
-
// host paint a cached score on cold start without waiting for
|
|
13
|
-
// the network. Stale entries are still served; the SDK
|
|
14
|
-
// re-fetches in the background.
|
|
15
|
-
//
|
|
16
|
-
// Fail-soft posture: if the network's down or the SDK isn't
|
|
17
|
-
// initialised, returns the cached value (if any), else the safe
|
|
18
|
-
// default (score = 100). The host should never see a thrown error
|
|
19
|
-
// from this call — the score system is supposed to be invisible.
|
|
20
|
-
import { isAnyNativeModuleLinked } from './native-loader';
|
|
21
|
-
import { getConfig, isInitialized } from './config';
|
|
22
|
-
import { getInstallId } from './install-id';
|
|
23
|
-
const SDK_VERSION = '0.0.0';
|
|
24
|
-
const STORAGE_KEY = '@sentori/trust-score';
|
|
25
|
-
const L1_TTL_MS = 30_000;
|
|
26
|
-
const L2_TTL_MS = 5 * 60_000;
|
|
27
|
-
let _l1 = null;
|
|
28
|
-
let _inflight = null;
|
|
29
|
-
function loadAsyncStorage() {
|
|
30
|
-
if (!isAnyNativeModuleLinked(['RNCAsyncStorage', 'AsyncStorageModule']))
|
|
31
|
-
return null;
|
|
32
|
-
try {
|
|
33
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
34
|
-
const mod = require('@react-native-async-storage/async-storage');
|
|
35
|
-
return mod.default ?? mod;
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
function safeDefault(installId) {
|
|
42
|
-
return {
|
|
43
|
-
computedAt: new Date().toISOString(),
|
|
44
|
-
installId,
|
|
45
|
-
score: 100,
|
|
46
|
-
signals: [],
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
async function readL2() {
|
|
50
|
-
const storage = loadAsyncStorage();
|
|
51
|
-
if (!storage)
|
|
52
|
-
return null;
|
|
53
|
-
try {
|
|
54
|
-
const raw = await storage.getItem(STORAGE_KEY);
|
|
55
|
-
if (!raw)
|
|
56
|
-
return null;
|
|
57
|
-
const parsed = JSON.parse(raw);
|
|
58
|
-
if (typeof parsed?.fetchedAt !== 'number' ||
|
|
59
|
-
typeof parsed?.score?.score !== 'number') {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
return parsed;
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
async function writeL2(entry) {
|
|
69
|
-
const storage = loadAsyncStorage();
|
|
70
|
-
if (!storage)
|
|
71
|
-
return;
|
|
72
|
-
try {
|
|
73
|
-
await storage.setItem(STORAGE_KEY, JSON.stringify(entry));
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
// best-effort
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
async function fetchFresh(installId) {
|
|
80
|
-
const config = getConfig();
|
|
81
|
-
if (!config)
|
|
82
|
-
return safeDefault(installId);
|
|
83
|
-
try {
|
|
84
|
-
const resp = await fetch(`${config.ingestUrl}/v1/security/score?installId=${encodeURIComponent(installId)}`, {
|
|
85
|
-
headers: {
|
|
86
|
-
Authorization: `Bearer ${config.token}`,
|
|
87
|
-
'Sentori-Sdk': `react-native/${SDK_VERSION}`,
|
|
88
|
-
},
|
|
89
|
-
method: 'GET',
|
|
90
|
-
});
|
|
91
|
-
if (!resp.ok)
|
|
92
|
-
return safeDefault(installId);
|
|
93
|
-
const parsed = (await resp.json());
|
|
94
|
-
return parsed;
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
return safeDefault(installId);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Resolve the device's trust score. Always resolves — never throws,
|
|
102
|
-
* never rejects. The two-layer cache means the first call after cold
|
|
103
|
-
* start usually serves a < 1 ms result while a background refresh
|
|
104
|
-
* keeps the value current.
|
|
105
|
-
*/
|
|
106
|
-
export async function queryTrustScore() {
|
|
107
|
-
if (!isInitialized()) {
|
|
108
|
-
const id = await getInstallId();
|
|
109
|
-
return safeDefault(id);
|
|
110
|
-
}
|
|
111
|
-
// L1 hit
|
|
112
|
-
const now = Date.now();
|
|
113
|
-
if (_l1 && now - _l1.fetchedAt < L1_TTL_MS) {
|
|
114
|
-
return _l1.score;
|
|
115
|
-
}
|
|
116
|
-
// Coalesce concurrent calls onto the same inflight fetch.
|
|
117
|
-
if (_inflight)
|
|
118
|
-
return _inflight;
|
|
119
|
-
_inflight = (async () => {
|
|
120
|
-
try {
|
|
121
|
-
const installId = await getInstallId();
|
|
122
|
-
// L2 hit — serve immediately, kick off background refresh
|
|
123
|
-
const l2 = await readL2();
|
|
124
|
-
if (l2 && now - l2.fetchedAt < L2_TTL_MS && l2.score.installId === installId) {
|
|
125
|
-
_l1 = l2;
|
|
126
|
-
// background refresh (don't block caller)
|
|
127
|
-
void (async () => {
|
|
128
|
-
const fresh = await fetchFresh(installId);
|
|
129
|
-
const entry = { fetchedAt: Date.now(), score: fresh };
|
|
130
|
-
_l1 = entry;
|
|
131
|
-
await writeL2(entry);
|
|
132
|
-
})();
|
|
133
|
-
return l2.score;
|
|
134
|
-
}
|
|
135
|
-
const fresh = await fetchFresh(installId);
|
|
136
|
-
const entry = { fetchedAt: Date.now(), score: fresh };
|
|
137
|
-
_l1 = entry;
|
|
138
|
-
await writeL2(entry);
|
|
139
|
-
return fresh;
|
|
140
|
-
}
|
|
141
|
-
finally {
|
|
142
|
-
_inflight = null;
|
|
143
|
-
}
|
|
144
|
-
})();
|
|
145
|
-
return _inflight;
|
|
146
|
-
}
|
|
147
|
-
export function __resetTrustScoreForTests() {
|
|
148
|
-
_l1 = null;
|
|
149
|
-
_inflight = null;
|
|
150
|
-
}
|
|
151
|
-
//# sourceMappingURL=trust-score.js.map
|
package/lib/trust-score.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"trust-score.js","sourceRoot":"","sources":["../src/trust-score.ts"],"names":[],"mappings":"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"}
|
package/lib/types.d.ts
DELETED
package/lib/types.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,GAAG,EACH,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,aAAa,EACb,MAAM,EACN,QAAQ,EACR,KAAK,EACL,SAAS,EACT,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,IAAI,EACJ,IAAI,GACL,MAAM,wBAAwB,CAAA"}
|
package/lib/types.js
DELETED
package/lib/types.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,2DAA2D"}
|
package/lib/uuid.d.ts
DELETED
package/lib/uuid.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA"}
|
package/lib/uuid.js
DELETED
package/lib/uuid.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"uuid.js","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA"}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* v2.3 — beforeSend hook unit coverage for the RN SDK.
|
|
3
|
-
*
|
|
4
|
-
* We exercise the dispatcher directly via the
|
|
5
|
-
* `__applyBeforeSendForTests` test hook rather than running the full
|
|
6
|
-
* captureException pipeline. The behaviour-level coverage of
|
|
7
|
-
* captureException's enqueue path lives in the JS SDK's `sdk.test.ts`
|
|
8
|
-
* (which mocks fetch); the dispatcher itself is the only RN-specific
|
|
9
|
-
* piece (RN uses the same NEVER-rule policy as JS, but the
|
|
10
|
-
* one-shot warn flag is per-SDK so we verify it in both).
|
|
11
|
-
*/
|
|
12
|
-
import { describe, expect, test } from 'bun:test';
|
|
13
|
-
|
|
14
|
-
import { __applyBeforeSendForTests as applyBeforeSend } from '../capture';
|
|
15
|
-
import type { Event } from '../types';
|
|
16
|
-
|
|
17
|
-
function freshEvent(message: string): Event {
|
|
18
|
-
return {
|
|
19
|
-
id: '019eaa00-0000-7000-8000-000000000001',
|
|
20
|
-
timestamp: '2026-06-03T07:00:00.000Z',
|
|
21
|
-
kind: 'message',
|
|
22
|
-
platform: 'javascript',
|
|
23
|
-
release: 'app@1.0.0+1',
|
|
24
|
-
environment: 'test',
|
|
25
|
-
device: { os: 'ios' },
|
|
26
|
-
app: { version: '1.0.0', build: '1' },
|
|
27
|
-
user: null,
|
|
28
|
-
tags: {},
|
|
29
|
-
breadcrumbs: [],
|
|
30
|
-
message,
|
|
31
|
-
} as Event;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
describe('applyBeforeSend (RN, v2.3)', () => {
|
|
35
|
-
test('no hook → returns event unchanged', () => {
|
|
36
|
-
const ev = freshEvent('passthrough');
|
|
37
|
-
expect(applyBeforeSend(ev, undefined)).toBe(ev);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('hook mutates: returned event is what the caller sees', () => {
|
|
41
|
-
const ev = freshEvent('please scrub');
|
|
42
|
-
const out = applyBeforeSend(ev, (e) => ({
|
|
43
|
-
...e,
|
|
44
|
-
tags: { ...e.tags, scrubbed: '1' },
|
|
45
|
-
}));
|
|
46
|
-
expect(out).not.toBeNull();
|
|
47
|
-
expect(out!.tags?.scrubbed).toBe('1');
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('hook returns null → dispatcher returns null (caller drops)', () => {
|
|
51
|
-
const ev = freshEvent('drop me');
|
|
52
|
-
expect(applyBeforeSend(ev, () => null)).toBeNull();
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('hook throws → dispatcher returns the original event (NEVER rule)', () => {
|
|
56
|
-
const ev = freshEvent('survives bad hook');
|
|
57
|
-
const out = applyBeforeSend(ev, () => {
|
|
58
|
-
throw new Error('hook boom');
|
|
59
|
-
});
|
|
60
|
-
expect(out).toBe(ev);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test('hook returns non-event garbage → dispatcher returns the original event', () => {
|
|
64
|
-
const ev = freshEvent('survives bad return');
|
|
65
|
-
const out = applyBeforeSend(
|
|
66
|
-
ev,
|
|
67
|
-
// @ts-expect-error — host returns garbage; v2.3 contract is "fall back unmodified"
|
|
68
|
-
() => 42,
|
|
69
|
-
);
|
|
70
|
-
expect(out).toBe(ev);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'bun:test';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
addBreadcrumb,
|
|
5
|
-
getBreadcrumbs,
|
|
6
|
-
__resetForTests,
|
|
7
|
-
} from '../breadcrumbs';
|
|
8
|
-
|
|
9
|
-
describe('breadcrumbs', () => {
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
__resetForTests();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('adds and retrieves a breadcrumb', () => {
|
|
15
|
-
addBreadcrumb({ type: 'log', data: { level: 'info', message: 'hi' } });
|
|
16
|
-
const crumbs = getBreadcrumbs();
|
|
17
|
-
expect(crumbs).toHaveLength(1);
|
|
18
|
-
expect(crumbs[0]?.type).toBe('log');
|
|
19
|
-
expect(crumbs[0]?.data.message).toBe('hi');
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('auto-generates ISO 8601 timestamp when not provided', () => {
|
|
23
|
-
addBreadcrumb({ type: 'nav', data: { from: 'a', to: 'b' } });
|
|
24
|
-
const crumbs = getBreadcrumbs();
|
|
25
|
-
expect(crumbs[0]?.timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('caps at 100 entries (oldest discarded)', () => {
|
|
29
|
-
for (let i = 0; i < 150; i++) {
|
|
30
|
-
addBreadcrumb({ type: 'custom', data: { i } });
|
|
31
|
-
}
|
|
32
|
-
const crumbs = getBreadcrumbs();
|
|
33
|
-
expect(crumbs).toHaveLength(100);
|
|
34
|
-
expect(crumbs[0]?.data.i).toBe(50);
|
|
35
|
-
expect(crumbs[99]?.data.i).toBe(149);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('returns a fresh copy on each getBreadcrumbs call', () => {
|
|
39
|
-
addBreadcrumb({ type: 'log', data: {} });
|
|
40
|
-
const a = getBreadcrumbs();
|
|
41
|
-
const b = getBreadcrumbs();
|
|
42
|
-
expect(a).not.toBe(b);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test';
|
|
2
|
-
|
|
3
|
-
import { __captureAndAttachReplayForTests } from '../capture';
|
|
4
|
-
import { setConfig, __resetForTests as resetConfig } from '../config';
|
|
5
|
-
import type { Event } from '../types';
|
|
6
|
-
|
|
7
|
-
// Insight 2026-05-18 rc.3 verify: walker is healthy, ticks land
|
|
8
|
-
// deep wireframe JSON with TextView text, but the `replay` kind
|
|
9
|
-
// never reaches `event.attachments`. Root cause was the inline
|
|
10
|
-
// `btoa(ndjson)` upload path — spec-compliant Hermes / Bun btoa
|
|
11
|
-
// throws `InvalidCharacterError` on any code point > 0xFF, and the
|
|
12
|
-
// surrounding catch swallowed the throw silently. rc.4 routes
|
|
13
|
-
// through `base64Utf8`, fixing both Latin-1 and UTF-8 inputs.
|
|
14
|
-
//
|
|
15
|
-
// These tests run the actual captureAndAttachReplay function the
|
|
16
|
-
// captureException pipeline runs. fetch is mocked at the
|
|
17
|
-
// transport.ts boundary so the upload landing isn't tested here
|
|
18
|
-
// (covered separately in screenshot.test.ts); what we care about
|
|
19
|
-
// is "does event.attachments grow when the input contains
|
|
20
|
-
// non-Latin-1 text?".
|
|
21
|
-
|
|
22
|
-
const origFetch = globalThis.fetch;
|
|
23
|
-
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
setConfig({
|
|
26
|
-
enabled: true,
|
|
27
|
-
environment: 'test',
|
|
28
|
-
errorSampleRate: null,
|
|
29
|
-
ingestUrl: 'http://localhost:18080',
|
|
30
|
-
release: 'app@1.0.0+1',
|
|
31
|
-
screenshotsEnabled: true,
|
|
32
|
-
sessionTrailEnabled: true,
|
|
33
|
-
token: 'st_pk_test',
|
|
34
|
-
traceSampleRate: null,
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
afterEach(() => {
|
|
39
|
-
globalThis.fetch = origFetch;
|
|
40
|
-
resetConfig();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
function makeEvent(): Event {
|
|
44
|
-
return {
|
|
45
|
-
id: '019eaa00-0000-7000-8000-000000000001',
|
|
46
|
-
timestamp: '2026-05-18T05:15:24.000Z',
|
|
47
|
-
kind: 'error',
|
|
48
|
-
platform: 'javascript',
|
|
49
|
-
release: 'app@1.0.0+1',
|
|
50
|
-
environment: 'test',
|
|
51
|
-
device: { os: 'android', osVersion: '36' },
|
|
52
|
-
app: {
|
|
53
|
-
version: '1.0.0',
|
|
54
|
-
build: '1',
|
|
55
|
-
framework: { name: 'react-native', version: '0.80.0' },
|
|
56
|
-
},
|
|
57
|
-
user: null,
|
|
58
|
-
tags: undefined,
|
|
59
|
-
breadcrumbs: [],
|
|
60
|
-
error: { type: 'Error', message: 'boom', stack: [], cause: null },
|
|
61
|
-
fingerprint: undefined,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function mockUploadSuccess(): { calls: { url: string; body: unknown }[] } {
|
|
66
|
-
const calls: { url: string; body: unknown }[] = [];
|
|
67
|
-
globalThis.fetch = mock(async (url: Request | string | URL, init?: RequestInit) => {
|
|
68
|
-
let body: unknown = undefined;
|
|
69
|
-
try {
|
|
70
|
-
body = init?.body ? JSON.parse(String(init.body)) : undefined;
|
|
71
|
-
} catch {
|
|
72
|
-
body = init?.body;
|
|
73
|
-
}
|
|
74
|
-
calls.push({ url: String(url), body });
|
|
75
|
-
return new Response(
|
|
76
|
-
JSON.stringify({
|
|
77
|
-
kind: 'replay',
|
|
78
|
-
mediaType: 'application/x-ndjson',
|
|
79
|
-
refId: '019e3000-7000-7000-8000-00000000aaaa',
|
|
80
|
-
sizeBytes: 200,
|
|
81
|
-
}),
|
|
82
|
-
{ headers: { 'content-type': 'application/json' }, status: 201 },
|
|
83
|
-
);
|
|
84
|
-
}) as typeof fetch;
|
|
85
|
-
return { calls };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
describe('captureAndAttachReplay — Insight rc.3 regression', () => {
|
|
89
|
-
test('Latin-1 ndjson attaches replay kind (control)', async () => {
|
|
90
|
-
const { calls } = mockUploadSuccess();
|
|
91
|
-
const event = makeEvent();
|
|
92
|
-
const ndjson =
|
|
93
|
-
'{"ts":1700000000,"width":390,"height":844,"nodes":[' +
|
|
94
|
-
'{"kind":"text","x":10,"y":20,"w":100,"h":20,"text":"Settings"}]}';
|
|
95
|
-
await __captureAndAttachReplayForTests(event, ndjson);
|
|
96
|
-
expect(calls.length).toBe(1);
|
|
97
|
-
expect(event.attachments?.length).toBe(1);
|
|
98
|
-
expect(event.attachments?.[0]?.kind).toBe('replay');
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
test('UTF-8 ndjson attaches replay kind (the rc.3 Android crash repro)', async () => {
|
|
102
|
-
// This is the bug: pre-rc.4 `btoa(ndjson)` threw on the
|
|
103
|
-
// Japanese characters below, the catch swallowed it, the
|
|
104
|
-
// attachment silently never landed. Post-rc.4 the helper
|
|
105
|
-
// handles UTF-8 and the attachment lands normally.
|
|
106
|
-
const { calls } = mockUploadSuccess();
|
|
107
|
-
const event = makeEvent();
|
|
108
|
-
const ndjson =
|
|
109
|
-
'{"ts":1700000000,"width":390,"height":844,"nodes":[' +
|
|
110
|
-
'{"kind":"text","x":10,"y":20,"w":100,"h":20,"text":"設定"},' +
|
|
111
|
-
'{"kind":"text","x":10,"y":50,"w":100,"h":20,"text":"ログアウト"}]}';
|
|
112
|
-
await __captureAndAttachReplayForTests(event, ndjson);
|
|
113
|
-
expect(calls.length).toBe(1);
|
|
114
|
-
expect(event.attachments?.length).toBe(1);
|
|
115
|
-
expect(event.attachments?.[0]?.kind).toBe('replay');
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test('mixed em-dash / smart-quote / emoji attaches replay kind', async () => {
|
|
119
|
-
const { calls } = mockUploadSuccess();
|
|
120
|
-
const event = makeEvent();
|
|
121
|
-
const ndjson =
|
|
122
|
-
'{"ts":1,"width":390,"height":844,"nodes":[' +
|
|
123
|
-
'{"kind":"text","x":0,"y":0,"w":50,"h":20,"text":"hello — world"},' +
|
|
124
|
-
'{"kind":"text","x":0,"y":30,"w":50,"h":20,"text":"\\"quoted\\""},' +
|
|
125
|
-
'{"kind":"text","x":0,"y":60,"w":50,"h":20,"text":"🎉"}]}';
|
|
126
|
-
await __captureAndAttachReplayForTests(event, ndjson);
|
|
127
|
-
expect(calls.length).toBe(1);
|
|
128
|
-
expect(event.attachments?.length).toBe(1);
|
|
129
|
-
expect(event.attachments?.[0]?.kind).toBe('replay');
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test('upload returns null → no attachment, no throw', async () => {
|
|
133
|
-
globalThis.fetch = (async () =>
|
|
134
|
-
new Response('{"error":"tooLarge"}', { status: 413 })) as typeof fetch;
|
|
135
|
-
const event = makeEvent();
|
|
136
|
-
const ndjson = '{"ts":1,"nodes":[]}';
|
|
137
|
-
await __captureAndAttachReplayForTests(event, ndjson);
|
|
138
|
-
expect(event.attachments).toBeUndefined();
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
test('fetch throws (offline) → no attachment, no throw out of the function', async () => {
|
|
142
|
-
globalThis.fetch = (async () => {
|
|
143
|
-
throw new TypeError('Network request failed');
|
|
144
|
-
}) as typeof fetch;
|
|
145
|
-
const event = makeEvent();
|
|
146
|
-
const ndjson = '{"ts":1,"nodes":[]}';
|
|
147
|
-
await __captureAndAttachReplayForTests(event, ndjson);
|
|
148
|
-
expect(event.attachments).toBeUndefined();
|
|
149
|
-
});
|
|
150
|
-
});
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* v2.3 W6.3 — Sentry-compat translation layer coverage.
|
|
3
|
-
*
|
|
4
|
-
* We exercise the pure-function pieces directly via the test hooks
|
|
5
|
-
* exported from the compat module (parseDsn, mapCategoryToType,
|
|
6
|
-
* mapLevel). The stateful pieces — init / captureException /
|
|
7
|
-
* setUser dispatching to the native init + scope — are covered by
|
|
8
|
-
* the upstream Sentori-native tests (sdk.test, before-send.test);
|
|
9
|
-
* the compat layer only re-shapes the call arguments before
|
|
10
|
-
* delegating, so testing the re-shape is enough.
|
|
11
|
-
*/
|
|
12
|
-
import { describe, expect, test } from 'bun:test';
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
__mapCategoryToTypeForTests,
|
|
16
|
-
__mapLevelForTests,
|
|
17
|
-
__parseDsnForTests,
|
|
18
|
-
Severity,
|
|
19
|
-
} from '../compat/sentry';
|
|
20
|
-
|
|
21
|
-
describe('parseDsn', () => {
|
|
22
|
-
test('extracts token + ingestUrl from a Sentori DSN', () => {
|
|
23
|
-
const r = __parseDsnForTests(
|
|
24
|
-
'https://st_pk_testabcdef@ingest.sentori.golia.jp/1',
|
|
25
|
-
);
|
|
26
|
-
expect(r.token).toBe('st_pk_testabcdef');
|
|
27
|
-
expect(r.ingestUrl).toBe('https://ingest.sentori.golia.jp');
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
test('honours custom port + host', () => {
|
|
31
|
-
const r = __parseDsnForTests('https://st_pk_x@self-hosted.example.com:8443/42');
|
|
32
|
-
expect(r.token).toBe('st_pk_x');
|
|
33
|
-
expect(r.ingestUrl).toBe('https://self-hosted.example.com:8443');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('rejects non-Sentori token prefix with a clear pointer', () => {
|
|
37
|
-
expect(() =>
|
|
38
|
-
__parseDsnForTests('https://abcd1234@sentry.io/12345'),
|
|
39
|
-
).toThrow(/st_pk_/);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test('rejects DSN without user-info (no token)', () => {
|
|
43
|
-
expect(() => __parseDsnForTests('https://sentry.io/12345')).toThrow();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test('rejects malformed URL', () => {
|
|
47
|
-
expect(() => __parseDsnForTests('not-a-url')).toThrow(/not a valid URL/);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
describe('mapCategoryToType', () => {
|
|
52
|
-
test('user-interaction categories → user', () => {
|
|
53
|
-
for (const c of ['auth', 'click', 'gesture', 'input', 'touch', 'ui']) {
|
|
54
|
-
expect(__mapCategoryToTypeForTests(c)).toBe('user');
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('network categories → net', () => {
|
|
59
|
-
for (const c of ['fetch', 'http', 'xhr']) {
|
|
60
|
-
expect(__mapCategoryToTypeForTests(c)).toBe('net');
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test('navigation categories → nav', () => {
|
|
65
|
-
for (const c of ['nav', 'navigation', 'route']) {
|
|
66
|
-
expect(__mapCategoryToTypeForTests(c)).toBe('nav');
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test('log-shaped categories → log', () => {
|
|
71
|
-
for (const c of ['console', 'log', 'sentry']) {
|
|
72
|
-
expect(__mapCategoryToTypeForTests(c)).toBe('log');
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test('unknown category → custom', () => {
|
|
77
|
-
expect(__mapCategoryToTypeForTests('whatever')).toBe('custom');
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test('undefined → undefined (pass-through to native default)', () => {
|
|
81
|
-
expect(__mapCategoryToTypeForTests(undefined)).toBeUndefined();
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
describe('mapLevel', () => {
|
|
86
|
-
test("Sentry's 'critical' maps to Sentori's 'fatal'", () => {
|
|
87
|
-
expect(__mapLevelForTests('critical')).toBe('fatal');
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test("Sentry's 'log' maps to Sentori's 'info' (no separate Log level)", () => {
|
|
91
|
-
expect(__mapLevelForTests('log')).toBe('info');
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test('5-level syslog names pass through unchanged', () => {
|
|
95
|
-
expect(__mapLevelForTests('fatal')).toBe('fatal');
|
|
96
|
-
expect(__mapLevelForTests('error')).toBe('error');
|
|
97
|
-
expect(__mapLevelForTests('warning')).toBe('warning');
|
|
98
|
-
expect(__mapLevelForTests('info')).toBe('info');
|
|
99
|
-
expect(__mapLevelForTests('debug')).toBe('debug');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test('undefined → undefined', () => {
|
|
103
|
-
expect(__mapLevelForTests(undefined)).toBeUndefined();
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
describe('Severity export', () => {
|
|
108
|
-
test('Critical collapses onto fatal', () => {
|
|
109
|
-
expect(Severity.Critical).toBe('fatal');
|
|
110
|
-
});
|
|
111
|
-
test('Log collapses onto info', () => {
|
|
112
|
-
expect(Severity.Log).toBe('info');
|
|
113
|
-
});
|
|
114
|
-
test('standard levels are themselves', () => {
|
|
115
|
-
expect(Severity.Fatal).toBe('fatal');
|
|
116
|
-
expect(Severity.Error).toBe('error');
|
|
117
|
-
expect(Severity.Warning).toBe('warning');
|
|
118
|
-
expect(Severity.Info).toBe('info');
|
|
119
|
-
expect(Severity.Debug).toBe('debug');
|
|
120
|
-
});
|
|
121
|
-
});
|