@clue-ai/browser-sdk 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -0
- package/dist/authoring/overlay.d.ts +12 -0
- package/dist/authoring/overlay.js +468 -0
- package/dist/authoring/recording.d.ts +125 -0
- package/dist/authoring/recording.js +481 -0
- package/dist/authoring/service-logo.d.ts +1 -0
- package/dist/authoring/service-logo.generated.d.ts +1 -0
- package/dist/authoring/service-logo.generated.js +3 -0
- package/dist/authoring/service-logo.js +1 -0
- package/dist/authoring/session.d.ts +23 -0
- package/dist/authoring/session.js +127 -0
- package/dist/authoring/surface.d.ts +11 -0
- package/dist/authoring/surface.js +63 -0
- package/dist/authoring/toolbar-constants.d.ts +23 -0
- package/dist/authoring/toolbar-constants.js +42 -0
- package/dist/authoring/toolbar-drag.d.ts +29 -0
- package/dist/authoring/toolbar-drag.js +270 -0
- package/dist/authoring/toolbar-view.d.ts +21 -0
- package/dist/authoring/toolbar-view.js +2584 -0
- package/dist/capture/action.d.ts +2 -0
- package/dist/capture/action.js +62 -0
- package/dist/capture/dom.d.ts +23 -0
- package/dist/capture/dom.js +329 -0
- package/dist/capture/drag.d.ts +2 -0
- package/dist/capture/drag.js +75 -0
- package/dist/capture/error.d.ts +2 -0
- package/dist/capture/error.js +193 -0
- package/dist/capture/form.d.ts +2 -0
- package/dist/capture/form.js +137 -0
- package/dist/capture/frustration.d.ts +2 -0
- package/dist/capture/frustration.js +171 -0
- package/dist/capture/input.d.ts +2 -0
- package/dist/capture/input.js +109 -0
- package/dist/capture/location.d.ts +10 -0
- package/dist/capture/location.js +42 -0
- package/dist/capture/navigation.d.ts +2 -0
- package/dist/capture/navigation.js +100 -0
- package/dist/capture/network.d.ts +13 -0
- package/dist/capture/network.js +903 -0
- package/dist/capture/page.d.ts +2 -0
- package/dist/capture/page.js +78 -0
- package/dist/capture/performance.d.ts +2 -0
- package/dist/capture/performance.js +268 -0
- package/dist/context/account.d.ts +12 -0
- package/dist/context/account.js +129 -0
- package/dist/context/environment.d.ts +42 -0
- package/dist/context/environment.js +208 -0
- package/dist/context/identity.d.ts +14 -0
- package/dist/context/identity.js +123 -0
- package/dist/context/session.d.ts +28 -0
- package/dist/context/session.js +155 -0
- package/dist/context/tab.d.ts +22 -0
- package/dist/context/tab.js +142 -0
- package/dist/context/trace.d.ts +32 -0
- package/dist/context/trace.js +65 -0
- package/dist/core/config.d.ts +4 -0
- package/dist/core/config.js +199 -0
- package/dist/core/constants.d.ts +43 -0
- package/dist/core/constants.js +109 -0
- package/dist/core/contracts.d.ts +58 -0
- package/dist/core/contracts.js +53 -0
- package/dist/core/sdk.d.ts +2 -0
- package/dist/core/sdk.js +831 -0
- package/dist/core/types.d.ts +413 -0
- package/dist/core/types.js +1 -0
- package/dist/core/usage-governor.d.ts +7 -0
- package/dist/core/usage-governor.js +127 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +36 -0
- package/dist/integrations/next-router.d.ts +16 -0
- package/dist/integrations/next-router.js +18 -0
- package/dist/integrations/react-router.d.ts +7 -0
- package/dist/integrations/react-router.js +37 -0
- package/dist/internal/metrics.d.ts +9 -0
- package/dist/internal/metrics.js +38 -0
- package/dist/normalize/builders.d.ts +15 -0
- package/dist/normalize/builders.js +786 -0
- package/dist/normalize/canonical.d.ts +13 -0
- package/dist/normalize/canonical.js +77 -0
- package/dist/normalize/event-id.d.ts +8 -0
- package/dist/normalize/event-id.js +39 -0
- package/dist/normalize/path-template.d.ts +1 -0
- package/dist/normalize/path-template.js +33 -0
- package/dist/privacy/local-minimization.d.ts +29 -0
- package/dist/privacy/local-minimization.js +88 -0
- package/dist/privacy/mask.d.ts +7 -0
- package/dist/privacy/mask.js +60 -0
- package/dist/privacy/parameter-snapshot.d.ts +14 -0
- package/dist/privacy/parameter-snapshot.js +206 -0
- package/dist/privacy/sanitize.d.ts +11 -0
- package/dist/privacy/sanitize.js +145 -0
- package/dist/privacy/schema-evidence.d.ts +20 -0
- package/dist/privacy/schema-evidence.js +238 -0
- package/dist/transport/batch.d.ts +37 -0
- package/dist/transport/batch.js +182 -0
- package/dist/transport/client.d.ts +61 -0
- package/dist/transport/client.js +267 -0
- package/dist/transport/queue.d.ts +22 -0
- package/dist/transport/queue.js +56 -0
- package/dist/transport/retry.d.ts +14 -0
- package/dist/transport/retry.js +46 -0
- package/package.json +38 -0
|
@@ -0,0 +1,786 @@
|
|
|
1
|
+
import { FRONTEND_EVENT_CATEGORY, FRONTEND_EVENT_NAME, FRONTEND_EVENT_STATUS, } from "../core/contracts";
|
|
2
|
+
import { maskMessage, maskUnknown, maskUrl } from "../privacy/mask";
|
|
3
|
+
import { parseUrlForNetwork, sanitizeObject } from "../privacy/sanitize";
|
|
4
|
+
import { buildParameterSnapshotBundle, buildUnavailableParameterSnapshot, truncateParameterSnapshotValue, } from "../privacy/parameter-snapshot";
|
|
5
|
+
import { createCanonicalBaseEvent } from "./canonical";
|
|
6
|
+
const asString = (value) => {
|
|
7
|
+
return typeof value === "string" ? value : null;
|
|
8
|
+
};
|
|
9
|
+
const asBoolean = (value) => {
|
|
10
|
+
return typeof value === "boolean" ? value : null;
|
|
11
|
+
};
|
|
12
|
+
const asNumber = (value) => {
|
|
13
|
+
return typeof value === "number" && Number.isFinite(value)
|
|
14
|
+
? value
|
|
15
|
+
: undefined;
|
|
16
|
+
};
|
|
17
|
+
const countCaptureModes = (value) => {
|
|
18
|
+
const counts = {
|
|
19
|
+
maskedValueCount: 0,
|
|
20
|
+
forbiddenValueCount: 0,
|
|
21
|
+
truncatedValueCount: 0,
|
|
22
|
+
};
|
|
23
|
+
const walk = (current) => {
|
|
24
|
+
if (Array.isArray(current)) {
|
|
25
|
+
for (const child of current) {
|
|
26
|
+
walk(child);
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (!current || typeof current !== "object") {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const record = current;
|
|
34
|
+
switch (record.capture_mode) {
|
|
35
|
+
case "masked_fingerprint":
|
|
36
|
+
counts.maskedValueCount += 1;
|
|
37
|
+
return;
|
|
38
|
+
case "forbidden":
|
|
39
|
+
counts.forbiddenValueCount += 1;
|
|
40
|
+
return;
|
|
41
|
+
case "truncated":
|
|
42
|
+
counts.truncatedValueCount += 1;
|
|
43
|
+
return;
|
|
44
|
+
default:
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
for (const child of Object.values(record)) {
|
|
48
|
+
walk(child);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
walk(value);
|
|
52
|
+
return counts;
|
|
53
|
+
};
|
|
54
|
+
const asNonNegativeNumber = (value) => {
|
|
55
|
+
const numberValue = asNumber(value);
|
|
56
|
+
return numberValue !== undefined && numberValue >= 0
|
|
57
|
+
? Math.round(numberValue)
|
|
58
|
+
: null;
|
|
59
|
+
};
|
|
60
|
+
const asNonNegativeFinite = (value) => {
|
|
61
|
+
const numberValue = asNumber(value);
|
|
62
|
+
return numberValue !== undefined && numberValue >= 0 ? numberValue : null;
|
|
63
|
+
};
|
|
64
|
+
const asRecord = (value) => {
|
|
65
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
return {};
|
|
69
|
+
};
|
|
70
|
+
const asStringArray = (value) => {
|
|
71
|
+
if (!Array.isArray(value)) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
return value.filter((entry) => typeof entry === "string");
|
|
75
|
+
};
|
|
76
|
+
const setJsonStringEvidence = (target, sourceFields, key, sourceField, value) => {
|
|
77
|
+
const trimmed = value?.trim() ?? "";
|
|
78
|
+
if (!trimmed) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
target[key] = trimmed;
|
|
82
|
+
sourceFields.push(sourceField);
|
|
83
|
+
};
|
|
84
|
+
const uniqueStrings = (values) => {
|
|
85
|
+
return [...new Set(values)].sort();
|
|
86
|
+
};
|
|
87
|
+
const buildTargetObjectHintEvidence = (event) => {
|
|
88
|
+
const sourceFields = [];
|
|
89
|
+
const actionTarget = {};
|
|
90
|
+
const requestTarget = {};
|
|
91
|
+
setJsonStringEvidence(actionTarget, sourceFields, "stable_key", "stable_key", event.stable_key);
|
|
92
|
+
setJsonStringEvidence(actionTarget, sourceFields, "stable_key_quality", "stable_key_quality", event.stable_key_quality);
|
|
93
|
+
setJsonStringEvidence(actionTarget, sourceFields, "form_key", "form_key", event.form_key);
|
|
94
|
+
setJsonStringEvidence(actionTarget, sourceFields, "element_tag", "properties.element_tag", asString(event.properties.element_tag));
|
|
95
|
+
setJsonStringEvidence(actionTarget, sourceFields, "element_role", "properties.element_role", asString(event.properties.element_role));
|
|
96
|
+
setJsonStringEvidence(actionTarget, sourceFields, "element_label_candidate", "properties.element_label_candidate", asString(event.properties.element_label_candidate));
|
|
97
|
+
setJsonStringEvidence(actionTarget, sourceFields, "field_name_candidate", "properties.field_name_candidate", asString(event.properties.field_name_candidate));
|
|
98
|
+
setJsonStringEvidence(requestTarget, sourceFields, "method", "method", event.method);
|
|
99
|
+
setJsonStringEvidence(requestTarget, sourceFields, "path_template", "path_template", event.path_template);
|
|
100
|
+
setJsonStringEvidence(requestTarget, sourceFields, "path", "path", event.path);
|
|
101
|
+
setJsonStringEvidence(requestTarget, sourceFields, "target_service", "target_service", event.target_service);
|
|
102
|
+
if (sourceFields.length === 0) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const hint = {
|
|
106
|
+
evidence_source: "browser_sdk_local_capture",
|
|
107
|
+
source_fields: uniqueStrings(sourceFields),
|
|
108
|
+
};
|
|
109
|
+
if (Object.keys(actionTarget).length > 0) {
|
|
110
|
+
hint.action_target = actionTarget;
|
|
111
|
+
}
|
|
112
|
+
if (Object.keys(requestTarget).length > 0) {
|
|
113
|
+
hint.request_target = requestTarget;
|
|
114
|
+
}
|
|
115
|
+
return hint;
|
|
116
|
+
};
|
|
117
|
+
const buildBehaviorUnitHintEvidence = (event) => {
|
|
118
|
+
const sourceFields = ["event_name", "event_category"];
|
|
119
|
+
const hint = {
|
|
120
|
+
evidence_source: "browser_sdk_local_capture",
|
|
121
|
+
source_event_type: event.source_event_type ?? event.event_name,
|
|
122
|
+
source_event_kind: event.source_event_kind ?? event.event_category,
|
|
123
|
+
};
|
|
124
|
+
setJsonStringEvidence(hint, sourceFields, "interaction_id", "interaction_id", event.interaction_id);
|
|
125
|
+
setJsonStringEvidence(hint, sourceFields, "request_id", "request_id", event.request_id);
|
|
126
|
+
setJsonStringEvidence(hint, sourceFields, "request_span_id", "request_span_id", event.request_span_id);
|
|
127
|
+
setJsonStringEvidence(hint, sourceFields, "request_lifecycle_outcome", "attempt_status", event.attempt_status ?? null);
|
|
128
|
+
setJsonStringEvidence(hint, sourceFields, "stable_key", "stable_key", event.stable_key);
|
|
129
|
+
setJsonStringEvidence(hint, sourceFields, "form_key", "form_key", event.form_key);
|
|
130
|
+
setJsonStringEvidence(hint, sourceFields, "path_template", "path_template", event.path_template);
|
|
131
|
+
if (sourceFields.length <= 2) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
hint.source_fields = uniqueStrings(sourceFields);
|
|
135
|
+
return hint;
|
|
136
|
+
};
|
|
137
|
+
const applyLocalHintEvidence = (event) => {
|
|
138
|
+
const targetObjectHint = buildTargetObjectHintEvidence(event);
|
|
139
|
+
if (targetObjectHint) {
|
|
140
|
+
event.target_object_hint = targetObjectHint;
|
|
141
|
+
}
|
|
142
|
+
const behaviorUnitHint = buildBehaviorUnitHintEvidence(event);
|
|
143
|
+
if (behaviorUnitHint) {
|
|
144
|
+
event.properties.behavior_unit_hint_evidence = behaviorUnitHint;
|
|
145
|
+
}
|
|
146
|
+
return event;
|
|
147
|
+
};
|
|
148
|
+
const normalizeRequestLifecycleOutcome = (value) => {
|
|
149
|
+
switch (value) {
|
|
150
|
+
case "completed":
|
|
151
|
+
case "failed":
|
|
152
|
+
case "aborted":
|
|
153
|
+
case "unknown":
|
|
154
|
+
return value;
|
|
155
|
+
default:
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
const resolveRequestLifecycleOutcome = (input) => {
|
|
160
|
+
const explicitOutcome = normalizeRequestLifecycleOutcome(input.explicitOutcome);
|
|
161
|
+
if (explicitOutcome) {
|
|
162
|
+
return explicitOutcome;
|
|
163
|
+
}
|
|
164
|
+
const outcomeClass = input.outcomeClass?.toLowerCase() ?? "";
|
|
165
|
+
const failureType = input.failureType?.toLowerCase() ?? "";
|
|
166
|
+
if (outcomeClass === "aborted" || failureType.includes("abort")) {
|
|
167
|
+
return "aborted";
|
|
168
|
+
}
|
|
169
|
+
if (input.isFailed || failureType) {
|
|
170
|
+
return "failed";
|
|
171
|
+
}
|
|
172
|
+
if (typeof input.statusCode === "number") {
|
|
173
|
+
return input.statusCode >= 400 ? "failed" : "completed";
|
|
174
|
+
}
|
|
175
|
+
if (outcomeClass === "success") {
|
|
176
|
+
return "completed";
|
|
177
|
+
}
|
|
178
|
+
if (outcomeClass) {
|
|
179
|
+
return "failed";
|
|
180
|
+
}
|
|
181
|
+
return "unknown";
|
|
182
|
+
};
|
|
183
|
+
const normalizePathLike = (value) => {
|
|
184
|
+
if (!value) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
const trimmed = value.trim();
|
|
188
|
+
if (!trimmed) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
|
|
192
|
+
return parseUrlForNetwork(trimmed).path || "/";
|
|
193
|
+
}
|
|
194
|
+
return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
195
|
+
};
|
|
196
|
+
const normalizeFormKey = (value) => {
|
|
197
|
+
const trimmed = value?.trim() ?? "";
|
|
198
|
+
return trimmed || null;
|
|
199
|
+
};
|
|
200
|
+
const canonicalizeUrl = (value) => {
|
|
201
|
+
if (!value) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
const url = new URL(value, globalThis.location?.origin);
|
|
206
|
+
return `${url.origin}${url.pathname}`;
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return normalizePathLike(value);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
const normalizeStableKeyQuality = (value) => {
|
|
213
|
+
switch (value) {
|
|
214
|
+
case "official":
|
|
215
|
+
case "compat":
|
|
216
|
+
case "structural":
|
|
217
|
+
return value;
|
|
218
|
+
default:
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
const inferStableKeyQuality = (stableKey) => {
|
|
223
|
+
if (!stableKey) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
if (stableKey.startsWith("dtid:") || stableKey.startsWith("dqa:")) {
|
|
227
|
+
return "compat";
|
|
228
|
+
}
|
|
229
|
+
if (stableKey.startsWith("name:") || stableKey.startsWith("aria:")) {
|
|
230
|
+
return "structural";
|
|
231
|
+
}
|
|
232
|
+
if (stableKey.startsWith("struct:")) {
|
|
233
|
+
return "structural";
|
|
234
|
+
}
|
|
235
|
+
if (stableKey.startsWith("id:")) {
|
|
236
|
+
return "structural";
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
};
|
|
240
|
+
const buildBaseEvent = (input) => {
|
|
241
|
+
return createCanonicalBaseEvent({
|
|
242
|
+
eventName: input.eventName,
|
|
243
|
+
eventCategory: input.eventCategory,
|
|
244
|
+
status: input.status,
|
|
245
|
+
context: input.options.context,
|
|
246
|
+
occurredAtMs: input.raw.occurredAtMs,
|
|
247
|
+
requestId: input.requestId,
|
|
248
|
+
requestSpanId: input.requestSpanId,
|
|
249
|
+
interactionId: input.interactionId ?? asString(input.raw.payload.interactionId),
|
|
250
|
+
traceId: input.traceId,
|
|
251
|
+
});
|
|
252
|
+
};
|
|
253
|
+
const setStringProperty = (event, key, value) => {
|
|
254
|
+
if (value) {
|
|
255
|
+
event.properties[key] = value;
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
const setUnknownProperty = (event, key, value) => {
|
|
259
|
+
if (value !== null && value !== undefined) {
|
|
260
|
+
event.properties[key] = value;
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
const setNumericMetric = (event, key, value) => {
|
|
264
|
+
if (typeof value === "number") {
|
|
265
|
+
event.metrics[key] = value;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
const applySummaryContract = (event, payload) => {
|
|
269
|
+
event.collector_name = asString(payload.collectorName);
|
|
270
|
+
event.aggregation_kind = asString(payload.aggregationKind);
|
|
271
|
+
event.summary_window_ms = asNonNegativeNumber(payload.summaryWindowMs);
|
|
272
|
+
event.summary_count = asNonNegativeNumber(payload.summaryCount);
|
|
273
|
+
event.dropped_count = asNonNegativeNumber(payload.droppedCount);
|
|
274
|
+
event.sample_rate = asNonNegativeFinite(payload.sampleRate);
|
|
275
|
+
event.budget_window_ms = asNonNegativeNumber(payload.budgetWindowMs);
|
|
276
|
+
event.rate_limit_key = asString(payload.rateLimitKey);
|
|
277
|
+
setNumericMetric(event, "summary_count", event.summary_count ?? undefined);
|
|
278
|
+
setNumericMetric(event, "dropped_count", event.dropped_count ?? undefined);
|
|
279
|
+
setNumericMetric(event, "sample_rate", event.sample_rate ?? undefined);
|
|
280
|
+
return event;
|
|
281
|
+
};
|
|
282
|
+
const applyPerformanceSummaryContract = (event, payload) => {
|
|
283
|
+
event.performance_kind = asString(payload.performanceKind);
|
|
284
|
+
event.rating = asString(payload.rating);
|
|
285
|
+
event.metric_name = asString(payload.metricName);
|
|
286
|
+
event.metric_value_bucket = asString(payload.metricValueBucket);
|
|
287
|
+
event.long_task_count = asNonNegativeNumber(payload.longTaskCount);
|
|
288
|
+
event.resource_failure_count = asNonNegativeNumber(payload.resourceFailureCount);
|
|
289
|
+
event.slow_resource_count = asNonNegativeNumber(payload.slowResourceCount);
|
|
290
|
+
event.worst_duration_bucket = asString(payload.worstDurationBucket);
|
|
291
|
+
setStringProperty(event, "performance_kind", event.performance_kind);
|
|
292
|
+
setStringProperty(event, "rating", event.rating);
|
|
293
|
+
setStringProperty(event, "metric_name", event.metric_name);
|
|
294
|
+
setStringProperty(event, "metric_value_bucket", event.metric_value_bucket);
|
|
295
|
+
setStringProperty(event, "worst_duration_bucket", event.worst_duration_bucket);
|
|
296
|
+
setStringProperty(event, "load_duration_bucket", asString(payload.loadDurationBucket));
|
|
297
|
+
setStringProperty(event, "interaction_ready_ms_bucket", asString(payload.interactionReadyMsBucket));
|
|
298
|
+
setStringProperty(event, "long_task_count_bucket", asString(payload.longTaskCountBucket));
|
|
299
|
+
setStringProperty(event, "resource_failure_count_bucket", asString(payload.resourceFailureCountBucket));
|
|
300
|
+
setUnknownProperty(event, "web_vital_buckets", payload.webVitalBuckets);
|
|
301
|
+
setStringProperty(event, "flush_reason", asString(payload.flushReason));
|
|
302
|
+
setNumericMetric(event, "long_task_count", event.long_task_count ?? undefined);
|
|
303
|
+
setNumericMetric(event, "resource_failure_count", event.resource_failure_count ?? undefined);
|
|
304
|
+
setNumericMetric(event, "slow_resource_count", event.slow_resource_count ?? undefined);
|
|
305
|
+
return event;
|
|
306
|
+
};
|
|
307
|
+
const readFallbackPageUrl = (payload) => {
|
|
308
|
+
return (asString(payload.pageUrl) ??
|
|
309
|
+
asString(payload.url) ??
|
|
310
|
+
(typeof globalThis.location?.href === "string"
|
|
311
|
+
? globalThis.location.href
|
|
312
|
+
: null));
|
|
313
|
+
};
|
|
314
|
+
const readFallbackPath = (payload) => {
|
|
315
|
+
return (asString(payload.path) ??
|
|
316
|
+
asString(payload.route) ??
|
|
317
|
+
asString(payload.toPath) ??
|
|
318
|
+
normalizePathLike(asString(payload.pageUrl) ?? asString(payload.url)) ??
|
|
319
|
+
(typeof globalThis.location?.pathname === "string"
|
|
320
|
+
? globalThis.location.pathname
|
|
321
|
+
: null));
|
|
322
|
+
};
|
|
323
|
+
const applyCurrentPageContext = (event, raw) => {
|
|
324
|
+
const pageUrl = readFallbackPageUrl(raw.payload);
|
|
325
|
+
const path = readFallbackPath(raw.payload);
|
|
326
|
+
if (pageUrl && event.url_canonical == null) {
|
|
327
|
+
event.url_canonical = canonicalizeUrl(pageUrl);
|
|
328
|
+
}
|
|
329
|
+
if (path && event.path == null) {
|
|
330
|
+
event.path = path;
|
|
331
|
+
}
|
|
332
|
+
return event;
|
|
333
|
+
};
|
|
334
|
+
const applySemanticCaptureContract = (event, raw, options) => {
|
|
335
|
+
const payload = raw.payload;
|
|
336
|
+
const viewId = asString(payload.viewId);
|
|
337
|
+
const stableKey = asString(payload.stableKey);
|
|
338
|
+
const stableKeyQuality = normalizeStableKeyQuality(asString(payload.stableKeyQuality)) ??
|
|
339
|
+
inferStableKeyQuality(stableKey);
|
|
340
|
+
const formKey = normalizeFormKey(asString(payload.formKey) ??
|
|
341
|
+
asString(payload.formId) ??
|
|
342
|
+
asString(payload.formName));
|
|
343
|
+
const urlCanonical = canonicalizeUrl(asString(payload.urlCanonical) ??
|
|
344
|
+
asString(payload.pageUrl) ??
|
|
345
|
+
asString(payload.url) ??
|
|
346
|
+
event.url_canonical ??
|
|
347
|
+
null);
|
|
348
|
+
const referrerUrlCanonical = canonicalizeUrl(asString(payload.referrerUrlCanonical) ??
|
|
349
|
+
asString(payload.referrerUrl) ??
|
|
350
|
+
asString(payload.referrer) ??
|
|
351
|
+
options.context.referrer);
|
|
352
|
+
const screenKey = normalizePathLike(asString(payload.screenKey) ??
|
|
353
|
+
asString(payload.path) ??
|
|
354
|
+
asString(payload.route) ??
|
|
355
|
+
asString(payload.toPath)) ?? normalizePathLike(urlCanonical);
|
|
356
|
+
event.view_id = viewId;
|
|
357
|
+
event.screen_key = screenKey;
|
|
358
|
+
event.url_canonical = urlCanonical;
|
|
359
|
+
event.referrer_url_canonical = referrerUrlCanonical;
|
|
360
|
+
event.stable_key = stableKey;
|
|
361
|
+
event.stable_key_quality = stableKeyQuality;
|
|
362
|
+
event.form_key = formKey;
|
|
363
|
+
return event;
|
|
364
|
+
};
|
|
365
|
+
const sanitizeForCustomProperties = (value, options) => {
|
|
366
|
+
try {
|
|
367
|
+
const sanitized = sanitizeObject(value, options.deniedKeys);
|
|
368
|
+
if (sanitized &&
|
|
369
|
+
typeof sanitized === "object" &&
|
|
370
|
+
!Array.isArray(sanitized)) {
|
|
371
|
+
const properties = { ...sanitized };
|
|
372
|
+
delete properties.custom_event_name;
|
|
373
|
+
delete properties.customEventName;
|
|
374
|
+
return properties;
|
|
375
|
+
}
|
|
376
|
+
return sanitized;
|
|
377
|
+
}
|
|
378
|
+
catch (error) {
|
|
379
|
+
options.onMaskingError?.(error);
|
|
380
|
+
return {};
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
const buildSessionEvent = (raw, options) => {
|
|
384
|
+
const payload = raw.payload;
|
|
385
|
+
const isStart = raw.type === FRONTEND_EVENT_NAME.sessionStarted;
|
|
386
|
+
const event = buildBaseEvent({
|
|
387
|
+
raw,
|
|
388
|
+
eventName: raw.type,
|
|
389
|
+
eventCategory: FRONTEND_EVENT_CATEGORY.session,
|
|
390
|
+
status: FRONTEND_EVENT_STATUS.observed,
|
|
391
|
+
options,
|
|
392
|
+
});
|
|
393
|
+
const lifecycleUrl = canonicalizeUrl(asString(payload.entryUrl) ?? asString(payload.exitUrl));
|
|
394
|
+
if (lifecycleUrl) {
|
|
395
|
+
event.url_canonical = lifecycleUrl;
|
|
396
|
+
}
|
|
397
|
+
const sessionId = asString(payload.sessionId);
|
|
398
|
+
if (sessionId) {
|
|
399
|
+
event.session_id = sessionId;
|
|
400
|
+
}
|
|
401
|
+
setNumericMetric(event, "duration_ms", asNumber(payload.durationMs));
|
|
402
|
+
return event;
|
|
403
|
+
};
|
|
404
|
+
const buildNavigationEvent = (raw, options) => {
|
|
405
|
+
const payload = raw.payload;
|
|
406
|
+
const event = buildBaseEvent({
|
|
407
|
+
raw,
|
|
408
|
+
eventName: raw.type,
|
|
409
|
+
eventCategory: FRONTEND_EVENT_CATEGORY.navigation,
|
|
410
|
+
status: FRONTEND_EVENT_STATUS.observed,
|
|
411
|
+
options,
|
|
412
|
+
});
|
|
413
|
+
if (raw.type === FRONTEND_EVENT_NAME.pageView) {
|
|
414
|
+
event.view_id = asString(payload.viewId);
|
|
415
|
+
event.screen_key = normalizePathLike(asString(payload.screenKey));
|
|
416
|
+
event.path = normalizePathLike(asString(payload.path));
|
|
417
|
+
event.url_canonical = canonicalizeUrl(asString(payload.urlCanonical));
|
|
418
|
+
event.page_title_candidate = asString(payload.pageTitleCandidate);
|
|
419
|
+
event.route_name_candidate = asString(payload.routeNameCandidate);
|
|
420
|
+
event.primary_heading_candidate = asString(payload.primaryHeadingCandidate);
|
|
421
|
+
}
|
|
422
|
+
if (raw.type === FRONTEND_EVENT_NAME.pageLeave) {
|
|
423
|
+
event.view_id = asString(payload.viewId);
|
|
424
|
+
event.screen_key = normalizePathLike(asString(payload.screenKey));
|
|
425
|
+
event.path = normalizePathLike(asString(payload.path));
|
|
426
|
+
event.time_on_page_ms = asNumber(payload.timeOnPageMs) ?? null;
|
|
427
|
+
}
|
|
428
|
+
if (raw.type === FRONTEND_EVENT_NAME.routeChange) {
|
|
429
|
+
event.from_view_id = asString(payload.fromViewId);
|
|
430
|
+
event.to_view_id = asString(payload.toViewId);
|
|
431
|
+
event.from_screen_key = normalizePathLike(asString(payload.fromScreenKey));
|
|
432
|
+
event.to_screen_key = normalizePathLike(asString(payload.toScreenKey));
|
|
433
|
+
event.from_path = normalizePathLike(asString(payload.fromPath));
|
|
434
|
+
event.to_path = normalizePathLike(asString(payload.toPath));
|
|
435
|
+
}
|
|
436
|
+
return event;
|
|
437
|
+
};
|
|
438
|
+
const applyActionCommonPayload = (event, payload) => {
|
|
439
|
+
setStringProperty(event, "element_tag", asString(payload.elementTag));
|
|
440
|
+
setStringProperty(event, "element_role", asString(payload.elementRole));
|
|
441
|
+
setStringProperty(event, "element_id", asString(payload.elementId));
|
|
442
|
+
setUnknownProperty(event, "element_class", payload.elementClass);
|
|
443
|
+
setStringProperty(event, "field_name_candidate", asString(payload.fieldName));
|
|
444
|
+
setStringProperty(event, "field_type", asString(payload.fieldType));
|
|
445
|
+
setStringProperty(event, "element_label_candidate", asString(payload.clickTargetText) ?? asString(payload.fieldDisplayLabel));
|
|
446
|
+
setStringProperty(event, "aria_label_candidate", asString(payload.clickTargetAriaLabel) ?? asString(payload.fieldAriaLabel));
|
|
447
|
+
setStringProperty(event, "placeholder_candidate", asString(payload.fieldPlaceholder));
|
|
448
|
+
setStringProperty(event, "section_heading", asString(payload.sectionHeading));
|
|
449
|
+
setUnknownProperty(event, "field_name_meta", payload.fieldNameMeta);
|
|
450
|
+
setUnknownProperty(event, "element_label_meta", payload.elementLabelMeta);
|
|
451
|
+
setUnknownProperty(event, "aria_label_meta", payload.ariaLabelMeta);
|
|
452
|
+
setUnknownProperty(event, "placeholder_meta", payload.placeholderMeta);
|
|
453
|
+
return event;
|
|
454
|
+
};
|
|
455
|
+
const buildActionEvent = (raw, options) => {
|
|
456
|
+
const payload = raw.payload;
|
|
457
|
+
const event = buildBaseEvent({
|
|
458
|
+
raw,
|
|
459
|
+
eventName: raw.type,
|
|
460
|
+
eventCategory: FRONTEND_EVENT_CATEGORY.action,
|
|
461
|
+
status: FRONTEND_EVENT_STATUS.observed,
|
|
462
|
+
options,
|
|
463
|
+
});
|
|
464
|
+
applySemanticCaptureContract(event, raw, options);
|
|
465
|
+
applyCurrentPageContext(event, raw);
|
|
466
|
+
applyActionCommonPayload(event, payload);
|
|
467
|
+
setUnknownProperty(event, "input_value_meta", payload.inputValueMeta);
|
|
468
|
+
setUnknownProperty(event, "selected_value_meta", payload.selectedValueMeta);
|
|
469
|
+
setUnknownProperty(event, "file_selection_meta", payload.fileSelectionMeta);
|
|
470
|
+
setUnknownProperty(event, "file_descriptors", payload.fileDescriptors);
|
|
471
|
+
setUnknownProperty(event, "drag_types", payload.dragTypes);
|
|
472
|
+
if (raw.type === FRONTEND_EVENT_NAME.formSubmitted) {
|
|
473
|
+
event.submit_result = asString(payload.submitResult);
|
|
474
|
+
event.result_source = asString(payload.resultSource);
|
|
475
|
+
setStringProperty(event, "submit_result", event.submit_result);
|
|
476
|
+
setStringProperty(event, "result_source", event.result_source);
|
|
477
|
+
setNumericMetric(event, "time_to_result_ms", asNumber(payload.timeToResultMs));
|
|
478
|
+
setNumericMetric(event, "invalid_field_count", asNumber(payload.invalidFieldCount));
|
|
479
|
+
setUnknownProperty(event, "invalid_field_stable_keys", payload.invalidFieldStableKeys);
|
|
480
|
+
setUnknownProperty(event, "validation_reason_codes", payload.validationReasonCodes);
|
|
481
|
+
setStringProperty(event, "validation_message_fingerprint", asString(payload.validationMessageFingerprint));
|
|
482
|
+
setUnknownProperty(event, "linked_request_span_ids", payload.linkedRequestSpanIds);
|
|
483
|
+
setStringProperty(event, "linked_backend_outcome_class", asString(payload.linkedBackendOutcomeClass));
|
|
484
|
+
}
|
|
485
|
+
const checked = asBoolean(payload.checked);
|
|
486
|
+
if (checked !== null) {
|
|
487
|
+
event.properties.checked = checked;
|
|
488
|
+
}
|
|
489
|
+
setNumericMetric(event, "selected_count", asNumber(payload.selectedCount));
|
|
490
|
+
setNumericMetric(event, "file_count", asNumber(payload.fileCount));
|
|
491
|
+
setNumericMetric(event, "drag_item_count", asNumber(payload.dragItemCount));
|
|
492
|
+
return applyLocalHintEvidence(event);
|
|
493
|
+
};
|
|
494
|
+
const buildRequestEvent = (raw, options) => {
|
|
495
|
+
const payload = raw.payload;
|
|
496
|
+
const isFailed = raw.type === FRONTEND_EVENT_NAME.requestFailed;
|
|
497
|
+
const event = buildBaseEvent({
|
|
498
|
+
raw,
|
|
499
|
+
eventName: raw.type === FRONTEND_EVENT_NAME.requestFinished
|
|
500
|
+
? FRONTEND_EVENT_NAME.requestFinished
|
|
501
|
+
: FRONTEND_EVENT_NAME.requestFailed,
|
|
502
|
+
eventCategory: FRONTEND_EVENT_CATEGORY.request,
|
|
503
|
+
status: isFailed
|
|
504
|
+
? FRONTEND_EVENT_STATUS.failed
|
|
505
|
+
: FRONTEND_EVENT_STATUS.finished,
|
|
506
|
+
options,
|
|
507
|
+
requestId: asString(payload.requestId),
|
|
508
|
+
requestSpanId: asString(payload.requestSpanId),
|
|
509
|
+
interactionId: asString(payload.interactionId),
|
|
510
|
+
traceId: asString(payload.traceId),
|
|
511
|
+
});
|
|
512
|
+
const rawUrl = asString(payload.url);
|
|
513
|
+
const parsedUrl = rawUrl ? parseUrlForNetwork(rawUrl) : null;
|
|
514
|
+
const path = normalizePathLike(asString(payload.path) ?? parsedUrl?.path ?? null);
|
|
515
|
+
const pathTemplate = normalizePathLike(asString(payload.pathTemplate) ?? parsedUrl?.pathTemplate ?? null) ?? "/";
|
|
516
|
+
const targetService = asString(payload.targetService) ??
|
|
517
|
+
asString(payload.target_service) ??
|
|
518
|
+
(parsedUrl?.host || null);
|
|
519
|
+
const statusCode = asNumber(payload.statusCode) ?? (isFailed ? 0 : undefined);
|
|
520
|
+
const requestParametersSource = payload.requestParametersSource ?? {};
|
|
521
|
+
const responseParametersSource = payload.responseParametersSource ?? {};
|
|
522
|
+
event.method = asString(payload.method);
|
|
523
|
+
event.path_template = pathTemplate;
|
|
524
|
+
event.path = path;
|
|
525
|
+
event.target_service = targetService;
|
|
526
|
+
setStringProperty(event, "failure_type", asString(payload.failureType));
|
|
527
|
+
event.status_code = statusCode ?? null;
|
|
528
|
+
event.duration_ms = asNumber(payload.durationMs) ?? null;
|
|
529
|
+
event.outcome_class = asString(payload.outcomeClass);
|
|
530
|
+
event.attempt_status = resolveRequestLifecycleOutcome({
|
|
531
|
+
isFailed,
|
|
532
|
+
statusCode: event.status_code,
|
|
533
|
+
failureType: asString(payload.failureType),
|
|
534
|
+
outcomeClass: event.outcome_class,
|
|
535
|
+
explicitOutcome: asString(payload.requestLifecycleOutcome),
|
|
536
|
+
});
|
|
537
|
+
event.request_schema_hash = asString(payload.requestSchemaHash);
|
|
538
|
+
event.response_schema_hash = asString(payload.responseSchemaHash);
|
|
539
|
+
event.request_shape_size = asNumber(payload.requestShapeSize) ?? null;
|
|
540
|
+
event.response_shape_size = asNumber(payload.responseShapeSize) ?? null;
|
|
541
|
+
setStringProperty(event, "outcome_class", event.outcome_class);
|
|
542
|
+
setStringProperty(event, "request_lifecycle_outcome", event.attempt_status);
|
|
543
|
+
setStringProperty(event, "request_kind", asString(payload.requestKind));
|
|
544
|
+
setStringProperty(event, "error_code_candidate", asString(payload.errorCodeCandidate));
|
|
545
|
+
setStringProperty(event, "request_schema_hash", event.request_schema_hash);
|
|
546
|
+
setStringProperty(event, "response_schema_hash", event.response_schema_hash);
|
|
547
|
+
setUnknownProperty(event, "request_field_paths", payload.requestFieldPaths);
|
|
548
|
+
setUnknownProperty(event, "response_field_paths", payload.responseFieldPaths);
|
|
549
|
+
setStringProperty(event, "graphql_operation_name", asString(payload.graphqlOperationName));
|
|
550
|
+
setStringProperty(event, "graphql_operation_type", asString(payload.graphqlOperationType));
|
|
551
|
+
setUnknownProperty(event, "graphql_top_level_fields", payload.graphqlTopLevelFields);
|
|
552
|
+
setNumericMetric(event, "request_shape_size", event.request_shape_size ?? undefined);
|
|
553
|
+
setNumericMetric(event, "response_shape_size", event.response_shape_size ?? undefined);
|
|
554
|
+
setNumericMetric(event, "request_size_bytes", asNumber(payload.requestSizeBytes));
|
|
555
|
+
setNumericMetric(event, "response_size_bytes", asNumber(payload.responseSizeBytes));
|
|
556
|
+
const requestMetadata = payload.requestMetadataSummary
|
|
557
|
+
? asRecord(payload.requestMetadataSummary)
|
|
558
|
+
: {};
|
|
559
|
+
requestMetadata.path_template = pathTemplate;
|
|
560
|
+
const queryKeys = asStringArray(payload.queryKeys);
|
|
561
|
+
if (queryKeys.length > 0) {
|
|
562
|
+
requestMetadata.query_keys = queryKeys;
|
|
563
|
+
}
|
|
564
|
+
if (Object.keys(requestMetadata).length > 0) {
|
|
565
|
+
event.request_metadata = requestMetadata;
|
|
566
|
+
}
|
|
567
|
+
if (payload.responseMetadataSummary) {
|
|
568
|
+
event.response_metadata = asRecord(payload.responseMetadataSummary);
|
|
569
|
+
}
|
|
570
|
+
try {
|
|
571
|
+
const requestSnapshot = buildParameterSnapshotBundle(requestParametersSource, {
|
|
572
|
+
allowedPaths: options.allowedValuePaths ?? [],
|
|
573
|
+
deniedKeys: options.deniedKeys,
|
|
574
|
+
});
|
|
575
|
+
const responseSnapshot = buildParameterSnapshotBundle(responseParametersSource, {
|
|
576
|
+
allowedPaths: options.allowedValuePaths ?? [],
|
|
577
|
+
deniedKeys: options.deniedKeys,
|
|
578
|
+
pathPrefix: "response",
|
|
579
|
+
});
|
|
580
|
+
event.request_parameters_snapshot_json = requestSnapshot.snapshot;
|
|
581
|
+
event.response_parameters_snapshot_json = responseSnapshot.snapshot;
|
|
582
|
+
const allowedValueKeys = [
|
|
583
|
+
...requestSnapshot.allowedValueKeys,
|
|
584
|
+
...responseSnapshot.allowedValueKeys,
|
|
585
|
+
];
|
|
586
|
+
if (allowedValueKeys.length > 0) {
|
|
587
|
+
event.allowed_value_keys = [...new Set(allowedValueKeys)].sort();
|
|
588
|
+
}
|
|
589
|
+
const requestCaptureCounts = countCaptureModes(requestSnapshot.snapshot);
|
|
590
|
+
const responseCaptureCounts = countCaptureModes(responseSnapshot.snapshot);
|
|
591
|
+
event.masked_value_count =
|
|
592
|
+
requestCaptureCounts.maskedValueCount +
|
|
593
|
+
responseCaptureCounts.maskedValueCount;
|
|
594
|
+
event.forbidden_value_count =
|
|
595
|
+
requestCaptureCounts.forbiddenValueCount +
|
|
596
|
+
responseCaptureCounts.forbiddenValueCount;
|
|
597
|
+
event.truncated_value_count =
|
|
598
|
+
requestCaptureCounts.truncatedValueCount +
|
|
599
|
+
responseCaptureCounts.truncatedValueCount;
|
|
600
|
+
setNumericMetric(event, "masked_value_count", event.masked_value_count);
|
|
601
|
+
setNumericMetric(event, "forbidden_value_count", event.forbidden_value_count);
|
|
602
|
+
setNumericMetric(event, "truncated_value_count", event.truncated_value_count);
|
|
603
|
+
}
|
|
604
|
+
catch (error) {
|
|
605
|
+
options.onMaskingError?.(error);
|
|
606
|
+
event.request_parameters_snapshot_json =
|
|
607
|
+
buildUnavailableParameterSnapshot();
|
|
608
|
+
event.response_parameters_snapshot_json =
|
|
609
|
+
buildUnavailableParameterSnapshot();
|
|
610
|
+
}
|
|
611
|
+
applySemanticCaptureContract(event, raw, options);
|
|
612
|
+
applyCurrentPageContext(event, raw);
|
|
613
|
+
return applyLocalHintEvidence(event);
|
|
614
|
+
};
|
|
615
|
+
const buildErrorEvent = (raw, options) => {
|
|
616
|
+
const payload = raw.payload;
|
|
617
|
+
const event = buildBaseEvent({
|
|
618
|
+
raw,
|
|
619
|
+
eventName: raw.type,
|
|
620
|
+
eventCategory: FRONTEND_EVENT_CATEGORY.error,
|
|
621
|
+
status: FRONTEND_EVENT_STATUS.observed,
|
|
622
|
+
options,
|
|
623
|
+
});
|
|
624
|
+
applySemanticCaptureContract(event, raw, options);
|
|
625
|
+
applyCurrentPageContext(event, raw);
|
|
626
|
+
setStringProperty(event, "error_type", asString(payload.errorType));
|
|
627
|
+
setStringProperty(event, "error_fingerprint", asString(payload.errorFingerprint));
|
|
628
|
+
setStringProperty(event, "source_file_hint", asString(payload.sourceFileHint));
|
|
629
|
+
setStringProperty(event, "source_file", maskUrl(asString(payload.sourceFile)));
|
|
630
|
+
setStringProperty(event, "message", maskMessage(asString(payload.message)));
|
|
631
|
+
setUnknownProperty(event, "stack", maskUnknown(payload.stack));
|
|
632
|
+
setNumericMetric(event, "repeat_count", asNumber(payload.repeatCount));
|
|
633
|
+
setNumericMetric(event, "line_number", asNumber(payload.lineNumber));
|
|
634
|
+
setNumericMetric(event, "column_number", asNumber(payload.columnNumber));
|
|
635
|
+
return event;
|
|
636
|
+
};
|
|
637
|
+
const buildSupportEvent = (raw, options) => {
|
|
638
|
+
const payload = raw.payload;
|
|
639
|
+
const event = buildBaseEvent({
|
|
640
|
+
raw,
|
|
641
|
+
eventName: raw.type,
|
|
642
|
+
eventCategory: FRONTEND_EVENT_CATEGORY.support,
|
|
643
|
+
status: FRONTEND_EVENT_STATUS.observed,
|
|
644
|
+
options,
|
|
645
|
+
});
|
|
646
|
+
applySemanticCaptureContract(event, raw, options);
|
|
647
|
+
applyCurrentPageContext(event, raw);
|
|
648
|
+
applyActionCommonPayload(event, payload);
|
|
649
|
+
event.collector_name = asString(payload.collectorName);
|
|
650
|
+
setStringProperty(event, "collector_name", event.collector_name);
|
|
651
|
+
setStringProperty(event, "detail_reason", asString(payload.detailReason));
|
|
652
|
+
setStringProperty(event, "resource_kind", asString(payload.resourceKind));
|
|
653
|
+
setStringProperty(event, "initiator_type", asString(payload.initiatorType));
|
|
654
|
+
setStringProperty(event, "url_canonical", asString(payload.urlCanonical));
|
|
655
|
+
setNumericMetric(event, "repeat_count", asNumber(payload.repeatCount));
|
|
656
|
+
return event;
|
|
657
|
+
};
|
|
658
|
+
const buildSummaryEvent = (raw, options) => {
|
|
659
|
+
const payload = raw.payload;
|
|
660
|
+
const eventCategory = raw.type === FRONTEND_EVENT_NAME.performanceSummaryObserved
|
|
661
|
+
? FRONTEND_EVENT_CATEGORY.performance
|
|
662
|
+
: FRONTEND_EVENT_CATEGORY.support;
|
|
663
|
+
const event = buildBaseEvent({
|
|
664
|
+
raw,
|
|
665
|
+
eventName: raw.type,
|
|
666
|
+
eventCategory,
|
|
667
|
+
status: FRONTEND_EVENT_STATUS.observed,
|
|
668
|
+
options,
|
|
669
|
+
});
|
|
670
|
+
applySemanticCaptureContract(event, raw, options);
|
|
671
|
+
applyCurrentPageContext(event, raw);
|
|
672
|
+
applySummaryContract(event, payload);
|
|
673
|
+
if (raw.type === FRONTEND_EVENT_NAME.performanceSummaryObserved) {
|
|
674
|
+
applyPerformanceSummaryContract(event, payload);
|
|
675
|
+
}
|
|
676
|
+
setStringProperty(event, "detail_reason", asString(payload.detailReason));
|
|
677
|
+
setStringProperty(event, "report_type", asString(payload.reportType));
|
|
678
|
+
setStringProperty(event, "network_phase", asString(payload.networkPhase));
|
|
679
|
+
setStringProperty(event, "capture_disabled_reason", asString(payload.captureDisabledReason));
|
|
680
|
+
setUnknownProperty(event, "active_capture_modules", payload.activeCaptureModules);
|
|
681
|
+
setNumericMetric(event, "events_summarized_count", asNumber(payload.eventsSummarizedCount));
|
|
682
|
+
setNumericMetric(event, "sampling_dropped_count", asNumber(payload.samplingDroppedCount));
|
|
683
|
+
setNumericMetric(event, "transport_failed_count", asNumber(payload.transportFailedCount));
|
|
684
|
+
setNumericMetric(event, "transport_retry_count", asNumber(payload.transportRetryCount));
|
|
685
|
+
return event;
|
|
686
|
+
};
|
|
687
|
+
const buildCustomEvent = (raw, options) => {
|
|
688
|
+
const payload = raw.payload;
|
|
689
|
+
const metrics = asRecord(payload.metrics);
|
|
690
|
+
const event = buildBaseEvent({
|
|
691
|
+
raw,
|
|
692
|
+
eventName: FRONTEND_EVENT_NAME.customEmitted,
|
|
693
|
+
eventCategory: FRONTEND_EVENT_CATEGORY.custom,
|
|
694
|
+
status: FRONTEND_EVENT_STATUS.observed,
|
|
695
|
+
options,
|
|
696
|
+
});
|
|
697
|
+
const customEventName = asString(payload.eventName)?.trim() ?? "";
|
|
698
|
+
for (const [key, value] of Object.entries(metrics)) {
|
|
699
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
700
|
+
event.metrics[key] = value;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
const properties = sanitizeForCustomProperties(payload.properties, options);
|
|
704
|
+
if (properties &&
|
|
705
|
+
typeof properties === "object" &&
|
|
706
|
+
!Array.isArray(properties)) {
|
|
707
|
+
event.properties = {
|
|
708
|
+
...event.properties,
|
|
709
|
+
...properties,
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
if (customEventName) {
|
|
713
|
+
event.custom_event_name = customEventName;
|
|
714
|
+
}
|
|
715
|
+
applyCurrentPageContext(event, raw);
|
|
716
|
+
return event;
|
|
717
|
+
};
|
|
718
|
+
const cloneCanonicalEvent = (event) => {
|
|
719
|
+
return JSON.parse(JSON.stringify(event));
|
|
720
|
+
};
|
|
721
|
+
const hasParameterSnapshots = (event) => {
|
|
722
|
+
return (event.request_parameters_snapshot_json !== undefined ||
|
|
723
|
+
event.response_parameters_snapshot_json !== undefined);
|
|
724
|
+
};
|
|
725
|
+
const buildParameterSnapshotTruncatedCandidate = (event) => {
|
|
726
|
+
const truncated = cloneCanonicalEvent(event);
|
|
727
|
+
if (truncated.request_parameters_snapshot_json !== undefined) {
|
|
728
|
+
truncated.request_parameters_snapshot_json = truncateParameterSnapshotValue(truncated.request_parameters_snapshot_json);
|
|
729
|
+
}
|
|
730
|
+
if (truncated.response_parameters_snapshot_json !== undefined) {
|
|
731
|
+
truncated.response_parameters_snapshot_json =
|
|
732
|
+
truncateParameterSnapshotValue(truncated.response_parameters_snapshot_json);
|
|
733
|
+
}
|
|
734
|
+
truncated.properties.degradation_stage = "parameter_snapshot_truncated";
|
|
735
|
+
return truncated;
|
|
736
|
+
};
|
|
737
|
+
export const buildDegradedCanonicalCandidates = (event) => {
|
|
738
|
+
if (!hasParameterSnapshots(event)) {
|
|
739
|
+
return [];
|
|
740
|
+
}
|
|
741
|
+
return [
|
|
742
|
+
{
|
|
743
|
+
stage: "parameter_snapshot_truncated",
|
|
744
|
+
event: buildParameterSnapshotTruncatedCandidate(event),
|
|
745
|
+
},
|
|
746
|
+
];
|
|
747
|
+
};
|
|
748
|
+
export const buildCanonicalEvent = (raw, options) => {
|
|
749
|
+
switch (raw.type) {
|
|
750
|
+
case FRONTEND_EVENT_NAME.sessionStarted:
|
|
751
|
+
case FRONTEND_EVENT_NAME.sessionEnded:
|
|
752
|
+
return buildSessionEvent(raw, options);
|
|
753
|
+
case FRONTEND_EVENT_NAME.pageView:
|
|
754
|
+
case FRONTEND_EVENT_NAME.pageLeave:
|
|
755
|
+
case FRONTEND_EVENT_NAME.routeChange:
|
|
756
|
+
return buildNavigationEvent(raw, options);
|
|
757
|
+
case FRONTEND_EVENT_NAME.elementClicked:
|
|
758
|
+
case FRONTEND_EVENT_NAME.formSubmitted:
|
|
759
|
+
case FRONTEND_EVENT_NAME.inputCommitted:
|
|
760
|
+
case FRONTEND_EVENT_NAME.toggleChanged:
|
|
761
|
+
case FRONTEND_EVENT_NAME.selectionCommitted:
|
|
762
|
+
case FRONTEND_EVENT_NAME.fileSelected:
|
|
763
|
+
case FRONTEND_EVENT_NAME.dragDropCompleted:
|
|
764
|
+
return buildActionEvent(raw, options);
|
|
765
|
+
case FRONTEND_EVENT_NAME.requestFinished:
|
|
766
|
+
case FRONTEND_EVENT_NAME.requestFailed:
|
|
767
|
+
return buildRequestEvent(raw, options);
|
|
768
|
+
case FRONTEND_EVENT_NAME.performanceSummaryObserved:
|
|
769
|
+
case FRONTEND_EVENT_NAME.browserReportSummaryObserved:
|
|
770
|
+
case FRONTEND_EVENT_NAME.uiLifecycleSummaryObserved:
|
|
771
|
+
case FRONTEND_EVENT_NAME.streamLifecycleSummaryObserved:
|
|
772
|
+
case FRONTEND_EVENT_NAME.sdkDiagnosticObserved:
|
|
773
|
+
return buildSummaryEvent(raw, options);
|
|
774
|
+
case FRONTEND_EVENT_NAME.frontendError:
|
|
775
|
+
case FRONTEND_EVENT_NAME.unhandledRejection:
|
|
776
|
+
return buildErrorEvent(raw, options);
|
|
777
|
+
case FRONTEND_EVENT_NAME.deadClickDetected:
|
|
778
|
+
case FRONTEND_EVENT_NAME.errorClickDetected:
|
|
779
|
+
case FRONTEND_EVENT_NAME.rageClickDetected:
|
|
780
|
+
return buildSupportEvent(raw, options);
|
|
781
|
+
case FRONTEND_EVENT_NAME.customEmitted:
|
|
782
|
+
return buildCustomEvent(raw, options);
|
|
783
|
+
default:
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
};
|