@coralogix/react-native-plugin 0.7.2 → 0.9.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/CHANGELOG.md +22 -0
- package/CxSdk.podspec +3 -3
- package/README.md +146 -0
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/cxsdk/CxSdkModule.kt +8 -1
- package/index.cjs.js +159 -3
- package/index.esm.js +159 -4
- package/ios/CxSdk.swift +5 -3
- package/package.json +1 -1
- package/src/index.d.ts +1 -1
- package/src/model/CoralogixOtelWebType.d.ts +1 -1
- package/src/model/Types.d.ts +69 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
## 0.9.0 (2026-07-29)
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- add data & labels to React Native reportError
|
|
6
|
+
|
|
7
|
+
### ⬆️ Dependency Updates
|
|
8
|
+
|
|
9
|
+
- Native iOS SDK bumped to **2.13.2**
|
|
10
|
+
- Native Android SDK bumped to **2.19.2**
|
|
11
|
+
|
|
12
|
+
## 0.8.0 (2026-07-29)
|
|
13
|
+
|
|
14
|
+
### 🚀 Features
|
|
15
|
+
|
|
16
|
+
- expose isSessionSampledIn to beforeSend + make SDK-owned event fields read-only
|
|
17
|
+
|
|
18
|
+
### ⬆️ Dependency Updates
|
|
19
|
+
|
|
20
|
+
- Native iOS SDK bumped to **2.13.1**
|
|
21
|
+
- Native Android SDK bumped to **2.19.1**
|
|
22
|
+
|
|
1
23
|
## 0.7.2 (2026-07-20)
|
|
2
24
|
|
|
3
25
|
### 🩹 Fixes
|
package/CxSdk.podspec
CHANGED
|
@@ -16,9 +16,9 @@ Pod::Spec.new do |s|
|
|
|
16
16
|
|
|
17
17
|
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
18
18
|
|
|
19
|
-
s.dependency 'Coralogix','2.
|
|
20
|
-
s.dependency 'CoralogixInternal','2.
|
|
21
|
-
s.dependency 'SessionReplay','2.
|
|
19
|
+
s.dependency 'Coralogix','2.13.2'
|
|
20
|
+
s.dependency 'CoralogixInternal','2.13.2'
|
|
21
|
+
s.dependency 'SessionReplay','2.13.2'
|
|
22
22
|
|
|
23
23
|
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
|
|
24
24
|
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
|
package/README.md
CHANGED
|
@@ -92,6 +92,72 @@ CoralogixRum.error('this is an error log', {error: 'yes', is_bad: 'no'});
|
|
|
92
92
|
> ```
|
|
93
93
|
> in the event that you need to use any of these types please stringify them before you pass it to the `log` method
|
|
94
94
|
|
|
95
|
+
### Error Reporting
|
|
96
|
+
|
|
97
|
+
Report handled errors with optional structured data and labels. **Choose one of the variants below per error event** — each call generates a single error report.
|
|
98
|
+
|
|
99
|
+
**Parameters:**
|
|
100
|
+
- `error` (Error): The error object to report.
|
|
101
|
+
- `isCrash` (boolean): Whether this error represents a crash (`true`) or a handled exception (`false`).
|
|
102
|
+
- `data` (optional): Structured data object attached to the error event. Empty objects are omitted.
|
|
103
|
+
- `labels` (optional): Key-value pairs for categorizing the error event. Empty objects are omitted.
|
|
104
|
+
|
|
105
|
+
**Variant 1: Basic error reporting**
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
import { CoralogixRum } from '@coralogix/react-native-plugin';
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
await processPayment(order);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
CoralogixRum.reportError(error, false);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Variant 2: With structured data**
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
try {
|
|
121
|
+
await processPayment(order);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
CoralogixRum.reportError(error, false, {
|
|
124
|
+
orderId: '12345',
|
|
125
|
+
paymentMethod: 'visa',
|
|
126
|
+
amount: 99.99,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Variant 3: With data and labels**
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
try {
|
|
135
|
+
await processPayment(order);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
CoralogixRum.reportError(
|
|
138
|
+
error,
|
|
139
|
+
false,
|
|
140
|
+
{ orderId: '12345', amount: 99.99 },
|
|
141
|
+
{ team: 'payments', severity: 'high' }
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Reporting crashes:**
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
try {
|
|
150
|
+
await criticalOperation();
|
|
151
|
+
} catch (error) {
|
|
152
|
+
CoralogixRum.reportError(
|
|
153
|
+
error,
|
|
154
|
+
true, // isCrash = true
|
|
155
|
+
{ operation: 'criticalTask', stage: 'initialization' },
|
|
156
|
+
{ environment: 'production' }
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
95
161
|
### View Tracking
|
|
96
162
|
|
|
97
163
|
To track views, set the view context whenever a view changes.
|
|
@@ -184,6 +250,10 @@ await CoralogixRum.init({
|
|
|
184
250
|
Allowed values: `'errors'`, `'logs'`, `'network'`, `'userInteractions'`,
|
|
185
251
|
`'mobileVitals'`, `'customSpan'`, `'customMeasurement'`.
|
|
186
252
|
|
|
253
|
+
Inside a [`beforeSend`](#beforesend) callback, `session_context.isSessionSampledIn`
|
|
254
|
+
tells you whether an event came from a sampled-in session or reached you only via
|
|
255
|
+
`excludeFromSampling`, so excluded categories can be filtered further.
|
|
256
|
+
|
|
187
257
|
### Ignore Errors
|
|
188
258
|
|
|
189
259
|
The ignoreErrors option allows you to exclude errors that meet specific criteria.
|
|
@@ -353,6 +423,82 @@ await CoralogixRum.init({
|
|
|
353
423
|
});
|
|
354
424
|
```
|
|
355
425
|
|
|
426
|
+
#### Telling excluded events apart in `beforeSend`
|
|
427
|
+
|
|
428
|
+
Every event carries `session_context.isSessionSampledIn`. `false` means the event
|
|
429
|
+
reached you only because its category is listed in
|
|
430
|
+
[`excludeFromSampling`](#exclude-from-sampling) — the session itself was sampled
|
|
431
|
+
out. Use it to apply your own filtering on top of the exclude list, for example to
|
|
432
|
+
keep only error-severity events from sampled-out sessions:
|
|
433
|
+
|
|
434
|
+
```javascript
|
|
435
|
+
await CoralogixRum.init({
|
|
436
|
+
// ...
|
|
437
|
+
sessionSampleRate: 10,
|
|
438
|
+
excludeFromSampling: ['errors', 'logs'],
|
|
439
|
+
beforeSend: (event) => {
|
|
440
|
+
// Default to `true`: a native SDK older than the one this plugin pins does not
|
|
441
|
+
// stamp the flag, and `!undefined` would drop everything but errors.
|
|
442
|
+
const sampledIn = event.session_context.isSessionSampledIn ?? true;
|
|
443
|
+
|
|
444
|
+
// Session sampled out: forward only error-severity events, drop the rest.
|
|
445
|
+
if (
|
|
446
|
+
!sampledIn &&
|
|
447
|
+
event.event_context?.severity !== CoralogixLogSeverity.Error
|
|
448
|
+
) {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return event;
|
|
453
|
+
},
|
|
454
|
+
});
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
> **Note:** always read it as `isSessionSampledIn ?? true`. The field is absent on
|
|
458
|
+
> native SDKs that predate it, and the SDK never invents a value it was not given.
|
|
459
|
+
|
|
460
|
+
`isSessionSampledIn` is read-only: it reports the SDK's sampling decision for the
|
|
461
|
+
session, so assigning to it has no effect on the sent event. The decision is
|
|
462
|
+
recorded when the event is created, so events buffered across a session rotation
|
|
463
|
+
keep the decision of the session they belong to.
|
|
464
|
+
|
|
465
|
+
#### Read-only fields
|
|
466
|
+
|
|
467
|
+
Some fields exist for the callback to *read*, not to change. The SDK restores its
|
|
468
|
+
own values after `beforeSend` returns, so assigning to any of them — or injecting
|
|
469
|
+
one into a returned object — has no effect on the sent event.
|
|
470
|
+
|
|
471
|
+
Inside `session_context`, **only** the user fields are editable:
|
|
472
|
+
|
|
473
|
+
| Editable | Read-only |
|
|
474
|
+
| --- | --- |
|
|
475
|
+
| `user_id`, `user_name`, `user_email`, `user_metadata` | `session_id`, `session_creation_date`, `isSessionSampledIn`, `hasRecording` |
|
|
476
|
+
|
|
477
|
+
Any other key in `session_context` — including one your callback invents — is
|
|
478
|
+
discarded, so session identity cannot be forged.
|
|
479
|
+
|
|
480
|
+
Deleting an editable field is honored, since redaction-by-deletion is a valid use.
|
|
481
|
+
That means returning a *hand-built partial* `session_context` drops the user fields
|
|
482
|
+
you left out; edit the event you were given (or spread it) if you want to change
|
|
483
|
+
one field and keep the rest.
|
|
484
|
+
|
|
485
|
+
These top-level fields are read-only too, because rewriting them would corrupt
|
|
486
|
+
trace correlation, event dedup or product analytics rather than redact anything:
|
|
487
|
+
|
|
488
|
+
`spanId`, `traceId`, `fingerPrint`, `timestamp`, `platform`, `mobile_sdk`,
|
|
489
|
+
`snapshot_context`, `isSnapshotEvent`, `prev_session`, `view_number`,
|
|
490
|
+
`isNavigationEvent`
|
|
491
|
+
|
|
492
|
+
`view_number` and `isNavigationEvent` record what the SDK observed when it built
|
|
493
|
+
the event. They are deliberately not recomputed from `event_context.type`, so if
|
|
494
|
+
you relabel an event the two can differ — filter on `isNavigationEvent` when you
|
|
495
|
+
need "was this actually a navigation".
|
|
496
|
+
|
|
497
|
+
Everything else — `event_context` (including `severity`), `labels`,
|
|
498
|
+
`error_context`, `log_context`, `network_request_context`, `view_context`,
|
|
499
|
+
`environment`, `device_context`, `device_state`, `version_metadata` — is editable,
|
|
500
|
+
and returning `null` still drops the event entirely.
|
|
501
|
+
|
|
356
502
|
### Proxy URL
|
|
357
503
|
|
|
358
504
|
Proxy configuration to route requests.
|
package/android/build.gradle
CHANGED
|
@@ -84,7 +84,7 @@ dependencies {
|
|
|
84
84
|
implementation "com.facebook.react:react-android"
|
|
85
85
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
86
86
|
|
|
87
|
-
implementation "com.coralogix:android-sdk:2.
|
|
87
|
+
implementation "com.coralogix:android-sdk:2.19.2"
|
|
88
88
|
|
|
89
89
|
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
|
90
90
|
testImplementation "org.mockito:mockito-core:5.12.0"
|
|
@@ -182,13 +182,20 @@ class CxSdkModule(reactContext: ReactApplicationContext) :
|
|
|
182
182
|
?.mapValues { it.value as Any }
|
|
183
183
|
?.takeIf { it.isNotEmpty() }
|
|
184
184
|
|
|
185
|
+
val eventLabels: Map<String, Any>? = details.getMap("labels")
|
|
186
|
+
?.toStringAnyMap()
|
|
187
|
+
?.filterValues { it != null }
|
|
188
|
+
?.mapValues { it.value as Any }
|
|
189
|
+
?.takeIf { it.isNotEmpty() }
|
|
190
|
+
|
|
185
191
|
CoralogixRum.reportError(
|
|
186
192
|
CoralogixErrorDecorator(
|
|
187
193
|
errorType = errorType,
|
|
188
194
|
errorMessage = message,
|
|
189
195
|
errorStack = CoralogixStackTrace(parsedStackFrames),
|
|
190
196
|
isCrash = isCrash,
|
|
191
|
-
customAttributes = customAttributes
|
|
197
|
+
customAttributes = customAttributes,
|
|
198
|
+
labels = eventLabels
|
|
192
199
|
)
|
|
193
200
|
)
|
|
194
201
|
}
|
package/index.cjs.js
CHANGED
|
@@ -17,6 +17,18 @@ function _extends() {
|
|
|
17
17
|
}, _extends.apply(null, arguments);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
let CoralogixEventType = /*#__PURE__*/function (CoralogixEventType) {
|
|
21
|
+
CoralogixEventType["ERROR"] = "error";
|
|
22
|
+
CoralogixEventType["NETWORK_REQUEST"] = "network-request";
|
|
23
|
+
CoralogixEventType["LOG"] = "log";
|
|
24
|
+
CoralogixEventType["USER_INTERACTION"] = "user-interaction";
|
|
25
|
+
CoralogixEventType["WEB_VITALS"] = "web-vitals";
|
|
26
|
+
CoralogixEventType["LONG_TASK"] = "longtask";
|
|
27
|
+
CoralogixEventType["RESOURCES"] = "resources";
|
|
28
|
+
CoralogixEventType["INTERNAL"] = "internal";
|
|
29
|
+
CoralogixEventType["NAVIGATION"] = "navigation";
|
|
30
|
+
return CoralogixEventType;
|
|
31
|
+
}({});
|
|
20
32
|
let CoralogixLogSeverity = /*#__PURE__*/function (CoralogixLogSeverity) {
|
|
21
33
|
CoralogixLogSeverity[CoralogixLogSeverity["Debug"] = 1] = "Debug";
|
|
22
34
|
CoralogixLogSeverity[CoralogixLogSeverity["Verbose"] = 2] = "Verbose";
|
|
@@ -27,6 +39,27 @@ let CoralogixLogSeverity = /*#__PURE__*/function (CoralogixLogSeverity) {
|
|
|
27
39
|
return CoralogixLogSeverity;
|
|
28
40
|
}({});
|
|
29
41
|
|
|
42
|
+
/** Versions of the plugin and the underlying native SDK that produced an event. */
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A RUM event as it goes on the wire.
|
|
46
|
+
*
|
|
47
|
+
* Fields marked `readonly` are SDK-owned: span identity, dedup keys, SDK-owned
|
|
48
|
+
* counters and the SDK's own observations. `beforeSend` may read them, but the
|
|
49
|
+
* SDK's value is always restored after the callback returns — assigning to one
|
|
50
|
+
* has no effect on the sent event.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The view of an event that `beforeSend` receives.
|
|
55
|
+
*
|
|
56
|
+
* Only the user fields of `session_context` are editable — session identity, the
|
|
57
|
+
* sampling decision and the recording flag are SDK-owned and are restored from
|
|
58
|
+
* the SDK's own values after the callback returns. The same holds for the
|
|
59
|
+
* `readonly` fields inherited from {@link CxRumEvent}. Nothing a callback writes
|
|
60
|
+
* to those can reach the wire, so they cannot be forged.
|
|
61
|
+
*/
|
|
62
|
+
|
|
30
63
|
/**
|
|
31
64
|
* Instrumentation names that can be excluded from a custom tracer's span context.
|
|
32
65
|
* Pass one or more values to `getCustomTracer()` to prevent those event types from
|
|
@@ -544,7 +577,7 @@ function stopJsRefreshRateSampler() {
|
|
|
544
577
|
appStateSub = null;
|
|
545
578
|
}
|
|
546
579
|
|
|
547
|
-
var version = "0.
|
|
580
|
+
var version = "0.9.0";
|
|
548
581
|
var pkg = {
|
|
549
582
|
version: version};
|
|
550
583
|
|
|
@@ -1217,7 +1250,7 @@ const CoralogixRum = {
|
|
|
1217
1250
|
}
|
|
1218
1251
|
CxSdk.endTimeMeasure(name);
|
|
1219
1252
|
},
|
|
1220
|
-
reportError: (error, isCrash, data) => {
|
|
1253
|
+
reportError: (error, isCrash, data, labels) => {
|
|
1221
1254
|
if (!isInited) {
|
|
1222
1255
|
logger.debug('CoralogixRum must be initiated before error reporting');
|
|
1223
1256
|
return;
|
|
@@ -1231,6 +1264,9 @@ const CoralogixRum = {
|
|
|
1231
1264
|
if (data && Object.keys(data).length > 0) {
|
|
1232
1265
|
errorDetails.data = data;
|
|
1233
1266
|
}
|
|
1267
|
+
if (labels && Object.keys(labels).length > 0) {
|
|
1268
|
+
errorDetails.labels = labels;
|
|
1269
|
+
}
|
|
1234
1270
|
CxSdk.reportError(errorDetails);
|
|
1235
1271
|
}
|
|
1236
1272
|
};
|
|
@@ -1417,6 +1453,94 @@ const waitForFatalErrorFlush = async () => {
|
|
|
1417
1453
|
// Create event emitter
|
|
1418
1454
|
const eventEmitter = new reactNative.NativeEventEmitter(CxSdk);
|
|
1419
1455
|
|
|
1456
|
+
/**
|
|
1457
|
+
* The only `session_context` fields a `beforeSend` callback may change. Every
|
|
1458
|
+
* other field there is SDK-owned — session identity, the sampling decision, the
|
|
1459
|
+
* recording flag — and is restored from the SDK's own values after the callback
|
|
1460
|
+
* runs.
|
|
1461
|
+
*
|
|
1462
|
+
* Deliberately an allowlist rather than a list of protected fields: a session
|
|
1463
|
+
* field added by a future native SDK is then read-only by default instead of
|
|
1464
|
+
* silently becoming forgeable. Matches the Android SDK, whose `EditableCxRum`
|
|
1465
|
+
* only accepts a user context, so nothing else in the session can be rewritten.
|
|
1466
|
+
*/
|
|
1467
|
+
const EDITABLE_SESSION_FIELDS = ['user_id', 'user_name', 'user_email', 'user_metadata'];
|
|
1468
|
+
|
|
1469
|
+
/**
|
|
1470
|
+
* Top-level `cx_rum` keys a callback must not change: span identity, dedup keys,
|
|
1471
|
+
* SDK-owned counters and the SDK's own observations. A rewrite here would corrupt
|
|
1472
|
+
* trace correlation or downstream funnel analysis rather than redact anything.
|
|
1473
|
+
*
|
|
1474
|
+
* Mirrors the native SDKs' read-only set. `view_number` and `isNavigationEvent`
|
|
1475
|
+
* are included for the same reason they are natively: they record what the SDK
|
|
1476
|
+
* saw at build time, not a property derived from the (possibly relabelled) event.
|
|
1477
|
+
*/
|
|
1478
|
+
const READ_ONLY_CX_RUM_KEYS = ['snapshot_context', 'isSnapshotEvent', 'mobile_sdk', 'timestamp', 'traceId', 'spanId', 'fingerPrint', 'prev_session', 'platform', 'view_number', 'isNavigationEvent'];
|
|
1479
|
+
const isPlainObject = value => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
1480
|
+
|
|
1481
|
+
/**
|
|
1482
|
+
* Deep copy of a value that came off the native bridge, so a callback mutating a
|
|
1483
|
+
* nested object in place cannot also mutate the snapshot we restore from. Bridge
|
|
1484
|
+
* payloads are JSON by construction; anything exotic is returned as-is rather
|
|
1485
|
+
* than throwing, since a failed snapshot must not drop the event.
|
|
1486
|
+
*/
|
|
1487
|
+
const cloneBridgeValue = value => {
|
|
1488
|
+
if (value === null || typeof value !== 'object') {
|
|
1489
|
+
return value;
|
|
1490
|
+
}
|
|
1491
|
+
try {
|
|
1492
|
+
return JSON.parse(JSON.stringify(value));
|
|
1493
|
+
} catch (e) {
|
|
1494
|
+
return value;
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
|
|
1498
|
+
/**
|
|
1499
|
+
* Rebuilds `session_context` from the SDK's own values, copying over only the
|
|
1500
|
+
* fields in {@link EDITABLE_SESSION_FIELDS}. Anything the callback rewrote or
|
|
1501
|
+
* invented outside that set is discarded.
|
|
1502
|
+
*
|
|
1503
|
+
* @param merged `session_context` as it stands after the callback ran.
|
|
1504
|
+
* @param original `session_context` snapshotted *before* the callback ran.
|
|
1505
|
+
*/
|
|
1506
|
+
const rebuildSessionContext = (merged, original) => {
|
|
1507
|
+
// The SDK always sends a session_context. If it somehow did not, a callback
|
|
1508
|
+
// must not be able to invent one — session identity would be forgeable through
|
|
1509
|
+
// exactly the gap this function exists to close.
|
|
1510
|
+
if (!isPlainObject(original)) {
|
|
1511
|
+
return original;
|
|
1512
|
+
}
|
|
1513
|
+
// A callback that replaced session_context with a non-object (or dropped it)
|
|
1514
|
+
// gets the SDK's version back wholesale: there are no edits to salvage, and
|
|
1515
|
+
// native would otherwise fail to decode and drop the whole event.
|
|
1516
|
+
if (!isPlainObject(merged)) {
|
|
1517
|
+
return original;
|
|
1518
|
+
}
|
|
1519
|
+
const rebuilt = _extends({}, original);
|
|
1520
|
+
for (const key of EDITABLE_SESSION_FIELDS) {
|
|
1521
|
+
if (key in merged) {
|
|
1522
|
+
rebuilt[key] = merged[key];
|
|
1523
|
+
} else {
|
|
1524
|
+
// Removing a user field is a legitimate redaction, so honor the deletion.
|
|
1525
|
+
delete rebuilt[key];
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
return rebuilt;
|
|
1529
|
+
};
|
|
1530
|
+
|
|
1531
|
+
/** Puts the SDK's values back on the read-only top-level keys, in place. */
|
|
1532
|
+
const restoreReadOnlyCxRumKeys = (merged, protectedValues) => {
|
|
1533
|
+
for (const key of READ_ONLY_CX_RUM_KEYS) {
|
|
1534
|
+
if (key in protectedValues) {
|
|
1535
|
+
merged[key] = protectedValues[key];
|
|
1536
|
+
} else {
|
|
1537
|
+
// Native never sent this key, so a callback must not be able to fabricate
|
|
1538
|
+
// one that looks like it came from the SDK.
|
|
1539
|
+
delete merged[key];
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
};
|
|
1543
|
+
|
|
1420
1544
|
// Subscribe to beforeSend events
|
|
1421
1545
|
const subscription = eventEmitter.addListener('onBeforeSend', events => {
|
|
1422
1546
|
// logger.log("📨 onBeforeSend triggered with events: ", events)
|
|
@@ -1435,14 +1559,45 @@ const subscription = eventEmitter.addListener('onBeforeSend', events => {
|
|
|
1435
1559
|
const results = [];
|
|
1436
1560
|
for (const fullEvent of events) {
|
|
1437
1561
|
const event = fullEvent.text.cx_rum;
|
|
1562
|
+
|
|
1563
|
+
// Snapshot everything SDK-owned *before* the callback runs. Callbacks
|
|
1564
|
+
// routinely mutate the event in place (the documented redaction example
|
|
1565
|
+
// does) and `event` is the very object nested in `fullEvent`, so reading
|
|
1566
|
+
// these afterwards would just hand back the callback's own edits. The
|
|
1567
|
+
// snapshot is deep so an in-place edit to a nested object (say
|
|
1568
|
+
// `event.mobile_sdk.sdk_version`) can't tamper with it either.
|
|
1569
|
+
const protectedValues = {};
|
|
1570
|
+
if (isPlainObject(event)) {
|
|
1571
|
+
for (const key of READ_ONLY_CX_RUM_KEYS) {
|
|
1572
|
+
if (key in event) {
|
|
1573
|
+
protectedValues[key] = cloneBridgeValue(event[key]);
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
const protectedSessionContext = cloneBridgeValue(event == null ? void 0 : event.session_context);
|
|
1438
1578
|
const res = beforeSendCallback(event);
|
|
1439
1579
|
if (!res) {
|
|
1440
1580
|
continue;
|
|
1441
1581
|
}
|
|
1582
|
+
// A truthy non-object return means the callback malfunctioned. Discard its
|
|
1583
|
+
// edits rather than trusting it: spreading a string would splatter index
|
|
1584
|
+
// keys across cx_rum, and forwarding the event untouched would skip the
|
|
1585
|
+
// restores below — letting an in-place tamper through on the one path that
|
|
1586
|
+
// never gets checked.
|
|
1587
|
+
let edits = {};
|
|
1588
|
+
if (isPlainObject(res)) {
|
|
1589
|
+
edits = res;
|
|
1590
|
+
} else {
|
|
1591
|
+
logger.debug('⚠️ beforeSend returned a non-object; discarding its edits');
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1442
1594
|
// Merge the result from each event
|
|
1595
|
+
const mergedCxRum = _extends({}, fullEvent.text.cx_rum, edits);
|
|
1596
|
+
mergedCxRum.session_context = rebuildSessionContext(mergedCxRum.session_context, protectedSessionContext);
|
|
1597
|
+
restoreReadOnlyCxRumKeys(mergedCxRum, protectedValues);
|
|
1443
1598
|
const mergeEvent = _extends({}, fullEvent, {
|
|
1444
1599
|
text: _extends({}, fullEvent.text, {
|
|
1445
|
-
cx_rum:
|
|
1600
|
+
cx_rum: mergedCxRum
|
|
1446
1601
|
})
|
|
1447
1602
|
});
|
|
1448
1603
|
results.push(mergeEvent);
|
|
@@ -1462,6 +1617,7 @@ const subscription = eventEmitter.addListener('onBeforeSend', events => {
|
|
|
1462
1617
|
exports.CoralogixCustomSpan = CoralogixCustomSpan;
|
|
1463
1618
|
exports.CoralogixCustomTracer = CoralogixCustomTracer;
|
|
1464
1619
|
exports.CoralogixDomain = CoralogixDomain;
|
|
1620
|
+
exports.CoralogixEventType = CoralogixEventType;
|
|
1465
1621
|
exports.CoralogixGlobalSpan = CoralogixGlobalSpan;
|
|
1466
1622
|
exports.CoralogixLogSeverity = CoralogixLogSeverity;
|
|
1467
1623
|
exports.CoralogixRum = CoralogixRum;
|
package/index.esm.js
CHANGED
|
@@ -15,6 +15,18 @@ function _extends() {
|
|
|
15
15
|
}, _extends.apply(null, arguments);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
let CoralogixEventType = /*#__PURE__*/function (CoralogixEventType) {
|
|
19
|
+
CoralogixEventType["ERROR"] = "error";
|
|
20
|
+
CoralogixEventType["NETWORK_REQUEST"] = "network-request";
|
|
21
|
+
CoralogixEventType["LOG"] = "log";
|
|
22
|
+
CoralogixEventType["USER_INTERACTION"] = "user-interaction";
|
|
23
|
+
CoralogixEventType["WEB_VITALS"] = "web-vitals";
|
|
24
|
+
CoralogixEventType["LONG_TASK"] = "longtask";
|
|
25
|
+
CoralogixEventType["RESOURCES"] = "resources";
|
|
26
|
+
CoralogixEventType["INTERNAL"] = "internal";
|
|
27
|
+
CoralogixEventType["NAVIGATION"] = "navigation";
|
|
28
|
+
return CoralogixEventType;
|
|
29
|
+
}({});
|
|
18
30
|
let CoralogixLogSeverity = /*#__PURE__*/function (CoralogixLogSeverity) {
|
|
19
31
|
CoralogixLogSeverity[CoralogixLogSeverity["Debug"] = 1] = "Debug";
|
|
20
32
|
CoralogixLogSeverity[CoralogixLogSeverity["Verbose"] = 2] = "Verbose";
|
|
@@ -25,6 +37,27 @@ let CoralogixLogSeverity = /*#__PURE__*/function (CoralogixLogSeverity) {
|
|
|
25
37
|
return CoralogixLogSeverity;
|
|
26
38
|
}({});
|
|
27
39
|
|
|
40
|
+
/** Versions of the plugin and the underlying native SDK that produced an event. */
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* A RUM event as it goes on the wire.
|
|
44
|
+
*
|
|
45
|
+
* Fields marked `readonly` are SDK-owned: span identity, dedup keys, SDK-owned
|
|
46
|
+
* counters and the SDK's own observations. `beforeSend` may read them, but the
|
|
47
|
+
* SDK's value is always restored after the callback returns — assigning to one
|
|
48
|
+
* has no effect on the sent event.
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The view of an event that `beforeSend` receives.
|
|
53
|
+
*
|
|
54
|
+
* Only the user fields of `session_context` are editable — session identity, the
|
|
55
|
+
* sampling decision and the recording flag are SDK-owned and are restored from
|
|
56
|
+
* the SDK's own values after the callback returns. The same holds for the
|
|
57
|
+
* `readonly` fields inherited from {@link CxRumEvent}. Nothing a callback writes
|
|
58
|
+
* to those can reach the wire, so they cannot be forged.
|
|
59
|
+
*/
|
|
60
|
+
|
|
28
61
|
/**
|
|
29
62
|
* Instrumentation names that can be excluded from a custom tracer's span context.
|
|
30
63
|
* Pass one or more values to `getCustomTracer()` to prevent those event types from
|
|
@@ -542,7 +575,7 @@ function stopJsRefreshRateSampler() {
|
|
|
542
575
|
appStateSub = null;
|
|
543
576
|
}
|
|
544
577
|
|
|
545
|
-
var version = "0.
|
|
578
|
+
var version = "0.9.0";
|
|
546
579
|
var pkg = {
|
|
547
580
|
version: version};
|
|
548
581
|
|
|
@@ -1215,7 +1248,7 @@ const CoralogixRum = {
|
|
|
1215
1248
|
}
|
|
1216
1249
|
CxSdk.endTimeMeasure(name);
|
|
1217
1250
|
},
|
|
1218
|
-
reportError: (error, isCrash, data) => {
|
|
1251
|
+
reportError: (error, isCrash, data, labels) => {
|
|
1219
1252
|
if (!isInited) {
|
|
1220
1253
|
logger.debug('CoralogixRum must be initiated before error reporting');
|
|
1221
1254
|
return;
|
|
@@ -1229,6 +1262,9 @@ const CoralogixRum = {
|
|
|
1229
1262
|
if (data && Object.keys(data).length > 0) {
|
|
1230
1263
|
errorDetails.data = data;
|
|
1231
1264
|
}
|
|
1265
|
+
if (labels && Object.keys(labels).length > 0) {
|
|
1266
|
+
errorDetails.labels = labels;
|
|
1267
|
+
}
|
|
1232
1268
|
CxSdk.reportError(errorDetails);
|
|
1233
1269
|
}
|
|
1234
1270
|
};
|
|
@@ -1415,6 +1451,94 @@ const waitForFatalErrorFlush = async () => {
|
|
|
1415
1451
|
// Create event emitter
|
|
1416
1452
|
const eventEmitter = new NativeEventEmitter(CxSdk);
|
|
1417
1453
|
|
|
1454
|
+
/**
|
|
1455
|
+
* The only `session_context` fields a `beforeSend` callback may change. Every
|
|
1456
|
+
* other field there is SDK-owned — session identity, the sampling decision, the
|
|
1457
|
+
* recording flag — and is restored from the SDK's own values after the callback
|
|
1458
|
+
* runs.
|
|
1459
|
+
*
|
|
1460
|
+
* Deliberately an allowlist rather than a list of protected fields: a session
|
|
1461
|
+
* field added by a future native SDK is then read-only by default instead of
|
|
1462
|
+
* silently becoming forgeable. Matches the Android SDK, whose `EditableCxRum`
|
|
1463
|
+
* only accepts a user context, so nothing else in the session can be rewritten.
|
|
1464
|
+
*/
|
|
1465
|
+
const EDITABLE_SESSION_FIELDS = ['user_id', 'user_name', 'user_email', 'user_metadata'];
|
|
1466
|
+
|
|
1467
|
+
/**
|
|
1468
|
+
* Top-level `cx_rum` keys a callback must not change: span identity, dedup keys,
|
|
1469
|
+
* SDK-owned counters and the SDK's own observations. A rewrite here would corrupt
|
|
1470
|
+
* trace correlation or downstream funnel analysis rather than redact anything.
|
|
1471
|
+
*
|
|
1472
|
+
* Mirrors the native SDKs' read-only set. `view_number` and `isNavigationEvent`
|
|
1473
|
+
* are included for the same reason they are natively: they record what the SDK
|
|
1474
|
+
* saw at build time, not a property derived from the (possibly relabelled) event.
|
|
1475
|
+
*/
|
|
1476
|
+
const READ_ONLY_CX_RUM_KEYS = ['snapshot_context', 'isSnapshotEvent', 'mobile_sdk', 'timestamp', 'traceId', 'spanId', 'fingerPrint', 'prev_session', 'platform', 'view_number', 'isNavigationEvent'];
|
|
1477
|
+
const isPlainObject = value => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
1478
|
+
|
|
1479
|
+
/**
|
|
1480
|
+
* Deep copy of a value that came off the native bridge, so a callback mutating a
|
|
1481
|
+
* nested object in place cannot also mutate the snapshot we restore from. Bridge
|
|
1482
|
+
* payloads are JSON by construction; anything exotic is returned as-is rather
|
|
1483
|
+
* than throwing, since a failed snapshot must not drop the event.
|
|
1484
|
+
*/
|
|
1485
|
+
const cloneBridgeValue = value => {
|
|
1486
|
+
if (value === null || typeof value !== 'object') {
|
|
1487
|
+
return value;
|
|
1488
|
+
}
|
|
1489
|
+
try {
|
|
1490
|
+
return JSON.parse(JSON.stringify(value));
|
|
1491
|
+
} catch (e) {
|
|
1492
|
+
return value;
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
|
|
1496
|
+
/**
|
|
1497
|
+
* Rebuilds `session_context` from the SDK's own values, copying over only the
|
|
1498
|
+
* fields in {@link EDITABLE_SESSION_FIELDS}. Anything the callback rewrote or
|
|
1499
|
+
* invented outside that set is discarded.
|
|
1500
|
+
*
|
|
1501
|
+
* @param merged `session_context` as it stands after the callback ran.
|
|
1502
|
+
* @param original `session_context` snapshotted *before* the callback ran.
|
|
1503
|
+
*/
|
|
1504
|
+
const rebuildSessionContext = (merged, original) => {
|
|
1505
|
+
// The SDK always sends a session_context. If it somehow did not, a callback
|
|
1506
|
+
// must not be able to invent one — session identity would be forgeable through
|
|
1507
|
+
// exactly the gap this function exists to close.
|
|
1508
|
+
if (!isPlainObject(original)) {
|
|
1509
|
+
return original;
|
|
1510
|
+
}
|
|
1511
|
+
// A callback that replaced session_context with a non-object (or dropped it)
|
|
1512
|
+
// gets the SDK's version back wholesale: there are no edits to salvage, and
|
|
1513
|
+
// native would otherwise fail to decode and drop the whole event.
|
|
1514
|
+
if (!isPlainObject(merged)) {
|
|
1515
|
+
return original;
|
|
1516
|
+
}
|
|
1517
|
+
const rebuilt = _extends({}, original);
|
|
1518
|
+
for (const key of EDITABLE_SESSION_FIELDS) {
|
|
1519
|
+
if (key in merged) {
|
|
1520
|
+
rebuilt[key] = merged[key];
|
|
1521
|
+
} else {
|
|
1522
|
+
// Removing a user field is a legitimate redaction, so honor the deletion.
|
|
1523
|
+
delete rebuilt[key];
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
return rebuilt;
|
|
1527
|
+
};
|
|
1528
|
+
|
|
1529
|
+
/** Puts the SDK's values back on the read-only top-level keys, in place. */
|
|
1530
|
+
const restoreReadOnlyCxRumKeys = (merged, protectedValues) => {
|
|
1531
|
+
for (const key of READ_ONLY_CX_RUM_KEYS) {
|
|
1532
|
+
if (key in protectedValues) {
|
|
1533
|
+
merged[key] = protectedValues[key];
|
|
1534
|
+
} else {
|
|
1535
|
+
// Native never sent this key, so a callback must not be able to fabricate
|
|
1536
|
+
// one that looks like it came from the SDK.
|
|
1537
|
+
delete merged[key];
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
};
|
|
1541
|
+
|
|
1418
1542
|
// Subscribe to beforeSend events
|
|
1419
1543
|
const subscription = eventEmitter.addListener('onBeforeSend', events => {
|
|
1420
1544
|
// logger.log("📨 onBeforeSend triggered with events: ", events)
|
|
@@ -1433,14 +1557,45 @@ const subscription = eventEmitter.addListener('onBeforeSend', events => {
|
|
|
1433
1557
|
const results = [];
|
|
1434
1558
|
for (const fullEvent of events) {
|
|
1435
1559
|
const event = fullEvent.text.cx_rum;
|
|
1560
|
+
|
|
1561
|
+
// Snapshot everything SDK-owned *before* the callback runs. Callbacks
|
|
1562
|
+
// routinely mutate the event in place (the documented redaction example
|
|
1563
|
+
// does) and `event` is the very object nested in `fullEvent`, so reading
|
|
1564
|
+
// these afterwards would just hand back the callback's own edits. The
|
|
1565
|
+
// snapshot is deep so an in-place edit to a nested object (say
|
|
1566
|
+
// `event.mobile_sdk.sdk_version`) can't tamper with it either.
|
|
1567
|
+
const protectedValues = {};
|
|
1568
|
+
if (isPlainObject(event)) {
|
|
1569
|
+
for (const key of READ_ONLY_CX_RUM_KEYS) {
|
|
1570
|
+
if (key in event) {
|
|
1571
|
+
protectedValues[key] = cloneBridgeValue(event[key]);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
const protectedSessionContext = cloneBridgeValue(event == null ? void 0 : event.session_context);
|
|
1436
1576
|
const res = beforeSendCallback(event);
|
|
1437
1577
|
if (!res) {
|
|
1438
1578
|
continue;
|
|
1439
1579
|
}
|
|
1580
|
+
// A truthy non-object return means the callback malfunctioned. Discard its
|
|
1581
|
+
// edits rather than trusting it: spreading a string would splatter index
|
|
1582
|
+
// keys across cx_rum, and forwarding the event untouched would skip the
|
|
1583
|
+
// restores below — letting an in-place tamper through on the one path that
|
|
1584
|
+
// never gets checked.
|
|
1585
|
+
let edits = {};
|
|
1586
|
+
if (isPlainObject(res)) {
|
|
1587
|
+
edits = res;
|
|
1588
|
+
} else {
|
|
1589
|
+
logger.debug('⚠️ beforeSend returned a non-object; discarding its edits');
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1440
1592
|
// Merge the result from each event
|
|
1593
|
+
const mergedCxRum = _extends({}, fullEvent.text.cx_rum, edits);
|
|
1594
|
+
mergedCxRum.session_context = rebuildSessionContext(mergedCxRum.session_context, protectedSessionContext);
|
|
1595
|
+
restoreReadOnlyCxRumKeys(mergedCxRum, protectedValues);
|
|
1441
1596
|
const mergeEvent = _extends({}, fullEvent, {
|
|
1442
1597
|
text: _extends({}, fullEvent.text, {
|
|
1443
|
-
cx_rum:
|
|
1598
|
+
cx_rum: mergedCxRum
|
|
1444
1599
|
})
|
|
1445
1600
|
});
|
|
1446
1601
|
results.push(mergeEvent);
|
|
@@ -1457,4 +1612,4 @@ const subscription = eventEmitter.addListener('onBeforeSend', events => {
|
|
|
1457
1612
|
}
|
|
1458
1613
|
});
|
|
1459
1614
|
|
|
1460
|
-
export { CoralogixCustomSpan, CoralogixCustomTracer, CoralogixDomain, CoralogixGlobalSpan, CoralogixLogSeverity, CoralogixRum, CxSdk, SessionReplay, attachReactNavigationObserver, parseErrorStack, waitForFatalErrorFlush };
|
|
1615
|
+
export { CoralogixCustomSpan, CoralogixCustomTracer, CoralogixDomain, CoralogixEventType, CoralogixGlobalSpan, CoralogixLogSeverity, CoralogixRum, CxSdk, SessionReplay, attachReactNavigationObserver, parseErrorStack, waitForFatalErrorFlush };
|
package/ios/CxSdk.swift
CHANGED
|
@@ -237,15 +237,17 @@ class CxSdk: RCTEventEmitter {
|
|
|
237
237
|
let message = dictionary[Keys.errorMessage.rawValue] as? String ?? ""
|
|
238
238
|
let errorType = dictionary[Keys.errorType.rawValue] as? String ?? "5"
|
|
239
239
|
let isCrash = dictionary[Keys.isCrash.rawValue] as? Bool ?? false
|
|
240
|
-
// Pass `data` only when non-empty — an empty dict would surface
|
|
241
|
-
// `error_context.
|
|
240
|
+
// Pass `data`/`labels` only when non-empty — an empty dict would surface an
|
|
241
|
+
// empty `error_context.error_custom_data` (or labels) on the emitted RUM event.
|
|
242
242
|
let customAttributes = (dictionary["data"] as? [String: Any]).flatMap { $0.isEmpty ? nil : $0 }
|
|
243
|
+
let labels = (dictionary["labels"] as? [String: Any]).flatMap { $0.isEmpty ? nil : $0 }
|
|
243
244
|
|
|
244
245
|
coralogixRum?.reportError(message: message,
|
|
245
246
|
stackTrace: stackTrace,
|
|
246
247
|
errorType: errorType,
|
|
247
248
|
isCrash: isCrash,
|
|
248
|
-
customAttributes: customAttributes
|
|
249
|
+
customAttributes: customAttributes,
|
|
250
|
+
labels: labels)
|
|
249
251
|
resolve("reportError success")
|
|
250
252
|
}
|
|
251
253
|
|
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -79,7 +79,7 @@ export { type ApplicationContextConfig } from './model/ApplicationContextConfig'
|
|
|
79
79
|
export { type ViewContextConfig } from './model/ViewContextConfig';
|
|
80
80
|
export { CoralogixDomain } from './model/CoralogixDomain';
|
|
81
81
|
export { type UserContextConfig } from './model/UserContextConfig';
|
|
82
|
-
export { type CoralogixBrowserSdkConfig, type CoralogixIgnoredInstrument, type ExcludableInstrumentation, type NetworkCaptureRule, type TraceExporterData, CoralogixLogSeverity, } from './model/Types';
|
|
82
|
+
export { type CoralogixBrowserSdkConfig, type CoralogixIgnoredInstrument, type ExcludableInstrumentation, type NetworkCaptureRule, type TraceExporterData, type EditableCxRumEvent, type BeforeSendResult, CoralogixEventType, CoralogixLogSeverity, } from './model/Types';
|
|
83
83
|
export { type CoralogixOtelWebOptionsInstrumentations } from './model/CoralogixOtelWebOptionsInstrumentations';
|
|
84
84
|
export { type CustomMeasurement } from './model/CustomMeasurement';
|
|
85
85
|
export { type NetworkRequestDetails } from './model/NetworkRequestDetails';
|
|
@@ -59,7 +59,7 @@ export interface CoralogixOtelWebType extends SendLog {
|
|
|
59
59
|
* Unknown names are dropped silently by the native SDK.
|
|
60
60
|
*/
|
|
61
61
|
endTimeMeasure: (name: string) => void;
|
|
62
|
-
reportError: (error: Error, isCrash: boolean, data?: Record<string, unknown>) => void;
|
|
62
|
+
reportError: (error: Error, isCrash: boolean, data?: Record<string, unknown>, labels?: Record<string, unknown>) => void;
|
|
63
63
|
/**
|
|
64
64
|
* Report a network request to the underlying Coralogix SDK
|
|
65
65
|
* @param details
|
package/src/model/Types.d.ts
CHANGED
|
@@ -28,6 +28,28 @@ export interface SessionContext extends UserMetadata {
|
|
|
28
28
|
device: string | undefined;
|
|
29
29
|
os: string | undefined;
|
|
30
30
|
osVersion: string | number | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Whether the session this event belongs to was sampled in. `false` means the
|
|
33
|
+
* event reached export only because its category is listed in
|
|
34
|
+
* `excludeFromSampling` while the session itself was sampled out.
|
|
35
|
+
*
|
|
36
|
+
* Read-only: it records the SDK's sampling decision for the session, so
|
|
37
|
+
* assigning to it inside `beforeSend` has no effect on the sent event. The
|
|
38
|
+
* decision is stamped on each event when it is created, so events buffered
|
|
39
|
+
* across a session rotation keep the decision of the session they belong to.
|
|
40
|
+
*
|
|
41
|
+
* Optional because a native SDK older than the version this plugin pins does
|
|
42
|
+
* not stamp it, and the SDK never fabricates a value it did not receive. Treat
|
|
43
|
+
* `undefined` as `true` — the native SDKs use the same default, since an
|
|
44
|
+
* unstamped event can only have reached export on a sampled-in session.
|
|
45
|
+
*/
|
|
46
|
+
readonly isSessionSampledIn?: boolean;
|
|
47
|
+
/** Whether Session Replay was recording for this session. Read-only. */
|
|
48
|
+
readonly hasRecording?: boolean;
|
|
49
|
+
/** Session identity. Read-only — the SDK owns it. */
|
|
50
|
+
readonly session_id?: string;
|
|
51
|
+
/** Session start time. Read-only — the SDK owns it. */
|
|
52
|
+
readonly session_creation_date?: number;
|
|
31
53
|
}
|
|
32
54
|
export interface DeviceState {
|
|
33
55
|
battery: string | undefined;
|
|
@@ -114,8 +136,25 @@ export interface SnapshotContext {
|
|
|
114
136
|
actionCount: number;
|
|
115
137
|
hasRecording: boolean;
|
|
116
138
|
}
|
|
139
|
+
/** Versions of the plugin and the underlying native SDK that produced an event. */
|
|
140
|
+
export interface MobileSdkMetadata {
|
|
141
|
+
readonly framework?: string;
|
|
142
|
+
readonly os?: string;
|
|
143
|
+
/** Version of `@coralogix/react-native-plugin`. */
|
|
144
|
+
readonly sdk_version?: string;
|
|
145
|
+
/** Version of the native iOS/Android Coralogix SDK. */
|
|
146
|
+
readonly native_version?: string;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* A RUM event as it goes on the wire.
|
|
150
|
+
*
|
|
151
|
+
* Fields marked `readonly` are SDK-owned: span identity, dedup keys, SDK-owned
|
|
152
|
+
* counters and the SDK's own observations. `beforeSend` may read them, but the
|
|
153
|
+
* SDK's value is always restored after the callback returns — assigning to one
|
|
154
|
+
* has no effect on the sent event.
|
|
155
|
+
*/
|
|
117
156
|
export interface CxRumEvent {
|
|
118
|
-
platform: string;
|
|
157
|
+
readonly platform: string;
|
|
119
158
|
version_metadata: VersionMetaData;
|
|
120
159
|
session_context: SessionContext;
|
|
121
160
|
device_context: DeviceContext;
|
|
@@ -125,16 +164,39 @@ export interface CxRumEvent {
|
|
|
125
164
|
error_context?: ErrorContext;
|
|
126
165
|
log_context?: LogContext;
|
|
127
166
|
network_request_context?: NetworkRequestContext;
|
|
128
|
-
snapshot_context?: SnapshotContext;
|
|
167
|
+
readonly snapshot_context?: SnapshotContext;
|
|
129
168
|
labels: CoralogixRumLabels;
|
|
130
|
-
spanId: string;
|
|
131
|
-
traceId: string;
|
|
169
|
+
readonly spanId: string;
|
|
170
|
+
readonly traceId: string;
|
|
132
171
|
environment: string;
|
|
133
|
-
timestamp: number;
|
|
134
|
-
isSnapshotEvent?: boolean;
|
|
172
|
+
readonly timestamp: number;
|
|
173
|
+
readonly isSnapshotEvent?: boolean;
|
|
174
|
+
/** SDK/framework versions. Read-only. */
|
|
175
|
+
readonly mobile_sdk?: MobileSdkMetadata;
|
|
176
|
+
/** Event dedup key. Read-only. */
|
|
177
|
+
readonly fingerPrint?: string;
|
|
178
|
+
/** Previous session identity, present only after a session rotation. Read-only. */
|
|
179
|
+
readonly prev_session?: Record<string, unknown>;
|
|
180
|
+
/** SDK-owned per-session navigation step counter. Read-only. */
|
|
181
|
+
readonly view_number?: number | null;
|
|
182
|
+
/**
|
|
183
|
+
* Whether the SDK observed a navigation when it built this event. Read-only,
|
|
184
|
+
* and intentionally not recomputed from `event_context.type` — it records what
|
|
185
|
+
* the SDK saw, so it can legitimately diverge from a relabelled event type.
|
|
186
|
+
*/
|
|
187
|
+
readonly isNavigationEvent?: boolean;
|
|
135
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* The view of an event that `beforeSend` receives.
|
|
191
|
+
*
|
|
192
|
+
* Only the user fields of `session_context` are editable — session identity, the
|
|
193
|
+
* sampling decision and the recording flag are SDK-owned and are restored from
|
|
194
|
+
* the SDK's own values after the callback returns. The same holds for the
|
|
195
|
+
* `readonly` fields inherited from {@link CxRumEvent}. Nothing a callback writes
|
|
196
|
+
* to those can reach the wire, so they cannot be forged.
|
|
197
|
+
*/
|
|
136
198
|
export interface EditableCxRumEvent extends Omit<CxRumEvent, 'session_context' | 'timestamp' | 'snapshot_context'> {
|
|
137
|
-
session_context: Pick<SessionContext, keyof UserMetadata>;
|
|
199
|
+
session_context: Pick<SessionContext, keyof UserMetadata | 'isSessionSampledIn'>;
|
|
138
200
|
}
|
|
139
201
|
export type BeforeSendResult = EditableCxRumEvent | null;
|
|
140
202
|
export interface NetworkCaptureRule {
|