@genn-inc/cluebase-frontend-sdk 0.0.2 → 0.0.6
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 +21 -0
- package/dist/_generated/{identity-pii-policy.d.ts → identity-pii-policy-text-contains.d.ts} +1 -1
- package/dist/_generated/{identity-pii-policy.js → identity-pii-policy-text-contains.js} +1 -1
- package/dist/_generated/identity-pii-policy-whole-value.d.ts +37 -0
- package/dist/_generated/identity-pii-policy-whole-value.js +106 -0
- package/dist/_generated/otel-span-conversion.js +1 -9
- package/dist/context/action-context.js +27 -7
- package/dist/context/browser-storage.js +1 -2
- package/dist/context/environment.js +5 -3
- package/dist/context/identity.js +5 -9
- package/dist/context/organization.d.ts +1 -1
- package/dist/context/organization.js +6 -8
- package/dist/context/session.js +6 -8
- package/dist/context/tab.js +8 -12
- package/dist/context/trace.js +2 -1
- package/dist/exporter/batch.d.ts +3 -1
- package/dist/exporter/batch.js +6 -10
- package/dist/exporter/browser-outbox-records.d.ts +1 -1
- package/dist/exporter/browser-outbox-records.js +15 -6
- package/dist/exporter/browser-outbox.d.ts +5 -2
- package/dist/exporter/browser-outbox.js +48 -11
- package/dist/exporter/client.d.ts +2 -2
- package/dist/exporter/client.js +15 -30
- package/dist/exporter/delivery-response-contract.js +67 -17
- package/dist/exporter/delivery-response.d.ts +3 -1
- package/dist/exporter/delivery-response.js +31 -20
- package/dist/exporter/diagnostics.d.ts +1 -1
- package/dist/exporter/diagnostics.js +4 -1
- package/dist/exporter/event-id.js +1 -3
- package/dist/exporter/frontend-resource.js +5 -4
- package/dist/exporter/observation-source-event-exporter.d.ts +1 -0
- package/dist/exporter/observation-source-event-exporter.js +5 -2
- package/dist/exporter/path-template.js +3 -1
- package/dist/exporter/raw-event-to-source-signal.js +4 -2
- package/dist/exporter/sanitize.d.ts +3 -2
- package/dist/exporter/sanitize.js +4 -3
- package/dist/exporter/sdk-core-wasm.d.ts +5 -0
- package/dist/exporter/sdk-core-wasm.js +18 -0
- package/dist/exporter/source-event.d.ts +1 -0
- package/dist/exporter/span-to-source-signal.d.ts +1 -1
- package/dist/exporter/span-to-source-signal.js +122 -82
- package/dist/index.d.ts +5 -4
- package/dist/index.js +23 -11
- package/dist/instrumentation/action.js +9 -8
- package/dist/instrumentation/dom.js +13 -21
- package/dist/instrumentation/drag.js +0 -3
- package/dist/instrumentation/error.js +3 -1
- package/dist/instrumentation/form.js +5 -6
- package/dist/instrumentation/input.js +4 -8
- package/dist/instrumentation/location.js +3 -1
- package/dist/instrumentation/network-request.js +1 -1
- package/dist/instrumentation/network-span-attributes.js +2 -2
- package/dist/instrumentation/network.js +7 -3
- package/dist/instrumentation/performance.js +1 -2
- package/dist/internal/internal-log.js +2 -1
- package/dist/provider/_version.d.ts +1 -1
- package/dist/provider/_version.js +1 -1
- package/dist/provider/config.js +0 -1
- package/dist/provider/constants.d.ts +1 -1
- package/dist/provider/constants.js +0 -1
- package/dist/provider/sdk-error.d.ts +7 -0
- package/dist/provider/sdk-error.js +48 -0
- package/dist/provider/sdk-event-pipeline.d.ts +2 -1
- package/dist/provider/sdk-event-pipeline.js +6 -3
- package/dist/provider/sdk-input-normalization.js +2 -2
- package/dist/provider/sdk-otel-pipeline.js +5 -4
- package/dist/provider/sdk-runtime-surfaces.d.ts +15 -12
- package/dist/provider/sdk-runtime-surfaces.js +8 -7
- package/dist/provider/sdk.js +81 -12
- package/dist/provider/types.d.ts +6 -5
- package/dist/wasm/pkg-web/cluebase_sdk_core_wasm.d.ts +41 -0
- package/dist/wasm/pkg-web/cluebase_sdk_core_wasm.js +267 -0
- package/dist/wasm/pkg-web/cluebase_sdk_core_wasm_bg.wasm +0 -0
- package/dist/wasm/pkg-web/cluebase_sdk_core_wasm_bg.wasm.d.ts +12 -0
- package/dist/wasm/pkg-web/package.json +15 -0
- package/package.json +10 -3
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { buildLocalTextDescriptor } from "../exporter/local-minimization.js";
|
|
2
|
-
import {
|
|
2
|
+
import { fingerprintText } from "../exporter/text-fingerprint.js";
|
|
3
3
|
import { buildPrimaryActionKey } from "./common.js";
|
|
4
|
+
import { extractFieldName, extractFieldType, getElementAriaLabel, getElementClassList, getElementLabel, getElementPlaceholder, getElementRole, getElementSectionHeading, isEditableElement, resolveClickDisplayTarget, resolveInteractionScopeDescriptor, resolveStableKeyDescriptor, } from "./dom.js";
|
|
4
5
|
import { getCurrentPageSnapshot } from "./location.js";
|
|
5
6
|
import { buildScreenActionNamingEvidence } from "./naming-evidence.js";
|
|
6
|
-
import { fingerprintText } from "../exporter/text-fingerprint.js";
|
|
7
7
|
const VALIDATION_REASON_KEYS = [
|
|
8
8
|
"valueMissing",
|
|
9
9
|
"typeMismatch",
|
|
@@ -57,9 +57,6 @@ export const startFormCapture = (context) => {
|
|
|
57
57
|
if (!(target instanceof HTMLFormElement)) {
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
60
|
-
if (context.shouldIgnoreElement?.(target)) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
60
|
try {
|
|
64
61
|
const formKey = target.id || target.getAttribute("name") || null;
|
|
65
62
|
const interactionScope = resolveInteractionScopeDescriptor(target);
|
|
@@ -75,14 +72,16 @@ export const startFormCapture = (context) => {
|
|
|
75
72
|
? "validation_blocked"
|
|
76
73
|
: "unknown";
|
|
77
74
|
const resultSource = validationEvidence.invalidFieldCount > 0 ? "browser_validation" : null;
|
|
75
|
+
const pageSnapshot = getCurrentPageSnapshot();
|
|
78
76
|
context.onEvent({
|
|
79
77
|
type: "form_submitted",
|
|
80
78
|
occurredAtMs: Date.now(),
|
|
81
79
|
payload: {
|
|
82
80
|
viewId: context.getViewId(),
|
|
81
|
+
screenKey: pageSnapshot.screenKey,
|
|
83
82
|
formId: target.id || null,
|
|
84
83
|
formKey,
|
|
85
|
-
primaryActionKey: buildPrimaryActionKey(
|
|
84
|
+
primaryActionKey: buildPrimaryActionKey(pageSnapshot.screenKey, formKey ?? submitterStableKey?.stableKey ?? null),
|
|
86
85
|
formName: target.getAttribute("name"),
|
|
87
86
|
interactionScopeKind: interactionScope?.interactionScopeKind ?? null,
|
|
88
87
|
interactionScopeKey: interactionScope?.interactionScopeKey ?? null,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { buildLocalTextDescriptor, buildLocalValueDescriptor, } from "../exporter/local-minimization.js";
|
|
2
|
-
import { extractFieldName, extractFieldType, getElementAriaLabel, getElementLabel, getElementPlaceholder, getElementRole, getElementSectionHeading, getElementValue, resolveInteractionScopeDescriptor, resolveStableKeyDescriptor, } from "./dom.js";
|
|
3
2
|
import { buildPrimaryActionKey } from "./common.js";
|
|
3
|
+
import { extractFieldName, extractFieldType, getElementAriaLabel, getElementLabel, getElementPlaceholder, getElementRole, getElementSectionHeading, getElementValue, resolveInteractionScopeDescriptor, resolveStableKeyDescriptor, } from "./dom.js";
|
|
4
4
|
import { getCurrentPageSnapshot } from "./location.js";
|
|
5
5
|
import { buildScreenActionNamingEvidence } from "./naming-evidence.js";
|
|
6
6
|
const buildFileDescriptors = (input) => {
|
|
@@ -28,23 +28,22 @@ export const startInputCapture = (context) => {
|
|
|
28
28
|
if (context.getConsent() === "denied") {
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
|
-
if (context.shouldIgnoreElement?.(target)) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
31
|
if (target instanceof HTMLInputElement && target.type === "password") {
|
|
35
32
|
return;
|
|
36
33
|
}
|
|
37
34
|
try {
|
|
38
35
|
const stableKey = resolveStableKeyDescriptor(target);
|
|
39
36
|
const interactionScope = resolveInteractionScopeDescriptor(target);
|
|
37
|
+
const pageSnapshot = getCurrentPageSnapshot();
|
|
40
38
|
const commonPayload = {
|
|
41
39
|
viewId: context.getViewId(),
|
|
40
|
+
screenKey: pageSnapshot.screenKey,
|
|
42
41
|
formId: stableKey.formKey,
|
|
43
42
|
formKey: stableKey.formKey,
|
|
44
43
|
fieldType: extractFieldType(target),
|
|
45
44
|
elementTag: target.tagName.toLowerCase(),
|
|
46
45
|
elementRole: getElementRole(target),
|
|
47
|
-
primaryActionKey: buildPrimaryActionKey(
|
|
46
|
+
primaryActionKey: buildPrimaryActionKey(pageSnapshot.screenKey, stableKey.stableKey),
|
|
48
47
|
stableKey: stableKey.stableKey,
|
|
49
48
|
stableKeyQuality: stableKey.stableKeyQuality,
|
|
50
49
|
interactionScopeKind: interactionScope?.interactionScopeKind ?? null,
|
|
@@ -138,9 +137,6 @@ export const startInputCapture = (context) => {
|
|
|
138
137
|
if (context.getConsent() === "denied") {
|
|
139
138
|
return;
|
|
140
139
|
}
|
|
141
|
-
if (context.shouldIgnoreElement?.(target)) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
140
|
dirtyControls.add(target);
|
|
145
141
|
};
|
|
146
142
|
const changeHandler = (event) => {
|
|
@@ -53,7 +53,9 @@ export const getPageSnapshotFromUrl = (href) => {
|
|
|
53
53
|
}
|
|
54
54
|
let path = null;
|
|
55
55
|
try {
|
|
56
|
-
path = href
|
|
56
|
+
path = href
|
|
57
|
+
? new URL(href, globalThis.location?.origin).pathname || "/"
|
|
58
|
+
: null;
|
|
57
59
|
}
|
|
58
60
|
catch {
|
|
59
61
|
path = null;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { buildLocalQuerySummary, buildLocalStructuredSummary, } from "../exporter/local-minimization.js";
|
|
2
2
|
import { buildParameterSnapshotBundle, } from "../exporter/parameter-snapshot.js";
|
|
3
|
-
import { allowedValuePathsFor } from "../provider/privacy-policy-store.js";
|
|
4
3
|
import { toPathTemplate } from "../exporter/path-template.js";
|
|
5
4
|
import { maybeParseJson, sanitizeHeaders } from "../exporter/sanitize.js";
|
|
6
5
|
import { buildSchemaEvidence, deriveOutcomeClass, extractErrorCodeCandidate, extractGraphqlEvidence, } from "../exporter/schema-evidence.js";
|
|
6
|
+
import { allowedValuePathsFor } from "../provider/privacy-policy-store.js";
|
|
7
7
|
import { currentPageUrl, resolveUrlAgainstCurrentLocation, stripQueryFromUrl, } from "./common.js";
|
|
8
8
|
const MAX_CAPTURED_RESPONSE_BODY_BYTES = 16 * 1024;
|
|
9
9
|
export const headersToRecord = (headers) => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
2
2
|
import { buildLocalStructuredSummary } from "../exporter/local-minimization.js";
|
|
3
|
-
import {
|
|
3
|
+
import { buildParameterSnapshotBundle } from "../exporter/parameter-snapshot.js";
|
|
4
4
|
import { sanitizeHeaders } from "../exporter/sanitize.js";
|
|
5
5
|
import { FRONTEND_EVENT_CATEGORY, FRONTEND_EVENT_NAME, FRONTEND_EVENT_STATUS, } from "../provider/contracts.js";
|
|
6
|
-
import { buildParameterSnapshotBundle } from "../exporter/parameter-snapshot.js";
|
|
7
6
|
import { allowedValuePathsFor } from "../provider/privacy-policy-store.js";
|
|
7
|
+
import { cluebaseAuthoredMetricAttribute, cluebaseAuthoredPropertyAttribute, } from "./cluebase-authored-attributes.js";
|
|
8
8
|
import { buildResponseShapeEvidence, } from "./network-request.js";
|
|
9
9
|
const PRIVACY_POLICY_VERSION = 1;
|
|
10
10
|
const setOptionalString = (span, key, value) => {
|
|
@@ -324,9 +324,12 @@ const failOpenRequestLifecycles = (failureType) => {
|
|
|
324
324
|
}
|
|
325
325
|
pendingRequestLifecycles.clear();
|
|
326
326
|
};
|
|
327
|
-
const trackFetchSpanEndWait = () => {
|
|
327
|
+
const trackFetchSpanEndWait = (capture) => {
|
|
328
328
|
const wait = new Promise((resolve) => {
|
|
329
|
-
setTimeout(
|
|
329
|
+
setTimeout(() => {
|
|
330
|
+
capture.context.requestFlush?.();
|
|
331
|
+
resolve();
|
|
332
|
+
}, FETCH_SPAN_END_SETTLE_MS);
|
|
330
333
|
});
|
|
331
334
|
pendingFetchSpanEndWaits.add(wait);
|
|
332
335
|
void wait.finally(() => pendingFetchSpanEndWaits.delete(wait));
|
|
@@ -400,7 +403,7 @@ const configureFetchInstrumentation = (capture) => {
|
|
|
400
403
|
observationContext: capture.context.getObservationContext?.() ?? null,
|
|
401
404
|
deniedKeys: capture.deniedKeys,
|
|
402
405
|
});
|
|
403
|
-
trackFetchSpanEndWait();
|
|
406
|
+
trackFetchSpanEndWait(capture);
|
|
404
407
|
markRequestLifecycleTerminal(meta);
|
|
405
408
|
capture.context.requestFlush?.();
|
|
406
409
|
},
|
|
@@ -448,6 +451,7 @@ const configureXhrInstrumentation = (capture) => {
|
|
|
448
451
|
observationContext: capture.context.getObservationContext?.() ?? null,
|
|
449
452
|
deniedKeys: capture.deniedKeys,
|
|
450
453
|
});
|
|
454
|
+
trackFetchSpanEndWait(capture);
|
|
451
455
|
markRequestLifecycleTerminal(meta);
|
|
452
456
|
capture.context.requestFlush?.();
|
|
453
457
|
},
|
|
@@ -91,8 +91,7 @@ const observeEntryType = (observers, entryType, callback) => {
|
|
|
91
91
|
observer.observe({ type: entryType, buffered: true });
|
|
92
92
|
observers.push(observer);
|
|
93
93
|
}
|
|
94
|
-
catch {
|
|
95
|
-
}
|
|
94
|
+
catch { }
|
|
96
95
|
};
|
|
97
96
|
const readPerformanceNavigation = () => {
|
|
98
97
|
if (typeof performance === "undefined") {
|
|
@@ -26,7 +26,8 @@ export function logSwallowed(operation, _cause, context = {}) {
|
|
|
26
26
|
const details = {};
|
|
27
27
|
for (const [key, value] of Object.entries(context)) {
|
|
28
28
|
if (SAFE_CONTEXT_KEYS.has(key) &&
|
|
29
|
-
(value === null ||
|
|
29
|
+
(value === null ||
|
|
30
|
+
typeof value === "boolean" ||
|
|
30
31
|
(typeof value === "number" && Number.isFinite(value)) ||
|
|
31
32
|
(typeof value === "string" && SAFE_TOKEN.test(value)))) {
|
|
32
33
|
details[key] = value;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION: "0.0.
|
|
1
|
+
export declare const SDK_VERSION: "0.0.6";
|
package/dist/provider/config.js
CHANGED
|
@@ -104,7 +104,6 @@ export const resolveConfig = (options) => {
|
|
|
104
104
|
localSetupCheckEndpoint: null,
|
|
105
105
|
projectKey: options.projectKey?.trim() ?? "",
|
|
106
106
|
producerId: DEFAULT_FRONTEND_SOURCE_IDENTIFIER,
|
|
107
|
-
transportProfile: DEFAULT_CONFIG.transportProfile,
|
|
108
107
|
autocapture: options.autocapture ?? DEFAULT_CONFIG.autocapture,
|
|
109
108
|
captureNetwork: options.captureNetwork ?? DEFAULT_CONFIG.captureNetwork,
|
|
110
109
|
captureErrors: options.captureErrors ?? DEFAULT_CONFIG.captureErrors,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { CluebaseConsentState, ResolvedCluebaseConfig } from "./types.js";
|
|
2
1
|
import { SDK_VERSION } from "./_version.js";
|
|
2
|
+
import type { CluebaseConsentState, ResolvedCluebaseConfig } from "./types.js";
|
|
3
3
|
export { SDK_VERSION };
|
|
4
4
|
export declare const SDK_NAME = "@genn-inc/cluebase-frontend-sdk";
|
|
5
5
|
export declare const EVENT_SOURCE: "frontend_sdk";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type SdkErrorDetails = {
|
|
2
|
+
error_name: string;
|
|
3
|
+
error_message: string;
|
|
4
|
+
error_stack?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const getSdkErrorDetails: (error: unknown) => SdkErrorDetails;
|
|
7
|
+
export declare const reportSdkError: (operation: string, error: unknown) => SdkErrorDetails;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const redactSensitiveText = (value) => value
|
|
2
|
+
.replace(/\bpk_[A-Za-z0-9_-]+\b/g, "[redacted-project-key]")
|
|
3
|
+
.replace(/\b(Bearer|Basic)\s+[A-Za-z0-9._~+/-]+=*/gi, "$1 [redacted]")
|
|
4
|
+
.replace(/(https?:\/\/[^\s?]+)\?[^\s]*/gi, "$1?[redacted]")
|
|
5
|
+
.slice(0, 500);
|
|
6
|
+
const readErrorField = (error, field) => {
|
|
7
|
+
try {
|
|
8
|
+
const value = error?.[field];
|
|
9
|
+
return typeof value === "string" ? value : undefined;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
export const getSdkErrorDetails = (error) => {
|
|
16
|
+
let fallbackMessage;
|
|
17
|
+
try {
|
|
18
|
+
fallbackMessage = String(error);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
fallbackMessage = "Unknown thrown value";
|
|
22
|
+
}
|
|
23
|
+
const stack = readErrorField(error, "stack");
|
|
24
|
+
return {
|
|
25
|
+
error_name: readErrorField(error, "name") || "UnknownError",
|
|
26
|
+
error_message: redactSensitiveText(readErrorField(error, "message") || fallbackMessage),
|
|
27
|
+
...(stack
|
|
28
|
+
? {
|
|
29
|
+
error_stack: stack
|
|
30
|
+
.split(/\r?\n/)
|
|
31
|
+
.slice(1, 9)
|
|
32
|
+
.map(redactSensitiveText)
|
|
33
|
+
.join("\n"),
|
|
34
|
+
}
|
|
35
|
+
: {}),
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
export const reportSdkError = (operation, error) => {
|
|
39
|
+
const details = getSdkErrorDetails(error);
|
|
40
|
+
try {
|
|
41
|
+
if (typeof console !== "undefined" && typeof console.error === "function") {
|
|
42
|
+
const stack = details.error_stack ? `\n${details.error_stack}` : "";
|
|
43
|
+
console.error(`[cluebase] ${operation} failed (${details.error_name}): ${details.error_message}${stack}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch { }
|
|
47
|
+
return details;
|
|
48
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { TraceManager } from "../context/trace.js";
|
|
2
1
|
import type { EnvironmentManager } from "../context/environment.js";
|
|
3
2
|
import type { IdentityManager } from "../context/identity.js";
|
|
4
3
|
import type { OrganizationManager } from "../context/organization.js";
|
|
5
4
|
import type { SessionManager } from "../context/session.js";
|
|
6
5
|
import type { TabManager } from "../context/tab.js";
|
|
6
|
+
import { TraceManager } from "../context/trace.js";
|
|
7
7
|
import type { BatchTransport } from "../exporter/batch.js";
|
|
8
8
|
import type { InternalMetricsStore } from "../internal/metrics.js";
|
|
9
9
|
import type { CluebaseConsentState, CluebaseContext, CluebaseInternalLogger, IngestUsageGovernorFeedback, RawCapturedEvent, ResolvedCluebaseConfig } from "./types.js";
|
|
@@ -11,6 +11,7 @@ export declare const OFFICIAL_CUSTOM_EVENT_NAME: {
|
|
|
11
11
|
readonly identityIdentified: "identity_identified";
|
|
12
12
|
readonly identityReset: "identity_reset";
|
|
13
13
|
readonly organizationAssociated: "organization_associated";
|
|
14
|
+
readonly externalAccountLinked: "external_account_linked";
|
|
14
15
|
readonly sdkInitialized: "sdk_initialized";
|
|
15
16
|
};
|
|
16
17
|
export type OfficialCustomEventProperties = Record<string, string | string[] | number>;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { TraceManager } from "../context/trace.js";
|
|
2
2
|
import { generateViewId } from "../exporter/event-id.js";
|
|
3
|
-
import { rawEventToSourceSignalEvent } from "../exporter/raw-event-to-source-signal.js";
|
|
4
3
|
import { createFrontendResourceAttributes } from "../exporter/frontend-resource.js";
|
|
4
|
+
import { rawEventToSourceSignalEvent } from "../exporter/raw-event-to-source-signal.js";
|
|
5
5
|
import { sanitizeObject, } from "../exporter/sanitize.js";
|
|
6
|
-
import { DEFAULT_INGEST_USAGE_GOVERNOR_FEEDBACK, hasUsageGovernorFeedbackChanged, isCostCapabilityDisabled, normalizeIngestUsageGovernorFeedback, } from "./usage-governor.js";
|
|
7
6
|
import { FRONTEND_EVENT_NAME } from "./contracts.js";
|
|
8
7
|
import { shouldSampleSession } from "./sampling.js";
|
|
9
8
|
import { stripQueryAndHashFromUrl, toStringOrNull, toSubjectProfile, } from "./sdk-input-normalization.js";
|
|
9
|
+
import { DEFAULT_INGEST_USAGE_GOVERNOR_FEEDBACK, hasUsageGovernorFeedbackChanged, isCostCapabilityDisabled, normalizeIngestUsageGovernorFeedback, } from "./usage-governor.js";
|
|
10
10
|
const TRACE_ROOT_EVENTS = new Set([
|
|
11
11
|
"page_view",
|
|
12
12
|
"route_change",
|
|
@@ -23,6 +23,7 @@ export const OFFICIAL_CUSTOM_EVENT_NAME = {
|
|
|
23
23
|
identityIdentified: "identity_identified",
|
|
24
24
|
identityReset: "identity_reset",
|
|
25
25
|
organizationAssociated: "organization_associated",
|
|
26
|
+
externalAccountLinked: "external_account_linked",
|
|
26
27
|
sdkInitialized: "sdk_initialized",
|
|
27
28
|
};
|
|
28
29
|
const OFFICIAL_CUSTOM_EVENT_NAMES = new Set(Object.values(OFFICIAL_CUSTOM_EVENT_NAME));
|
|
@@ -458,7 +459,9 @@ export class ProviderEventPipeline {
|
|
|
458
459
|
}
|
|
459
460
|
function sanitizeContextTraits(traits, deniedKeys, excludedKeys = new Set(), summary, pathPrefix = "", maskIdentityPii = true, preserveExplicitTraits = false) {
|
|
460
461
|
const sanitized = sanitizeObject(traits, deniedKeys, summary, pathPrefix, maskIdentityPii, preserveExplicitTraits);
|
|
461
|
-
if (typeof sanitized !== "object" ||
|
|
462
|
+
if (typeof sanitized !== "object" ||
|
|
463
|
+
sanitized === null ||
|
|
464
|
+
Array.isArray(sanitized)) {
|
|
462
465
|
return {};
|
|
463
466
|
}
|
|
464
467
|
return Object.fromEntries(Object.entries(sanitized).filter(([key]) => !excludedKeys.has(key)));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { detectWholeValuePII, hashIdentityValue } from "../exporter/sanitize.js";
|
|
2
2
|
const PUBLIC_CUSTOM_EVENT_NAME_PATTERN = /^[A-Za-z0-9_.:-]{1,128}$/;
|
|
3
3
|
const SAFE_DATA_IMAGE_MIME_TYPES = new Set([
|
|
4
4
|
"image/png",
|
|
@@ -80,7 +80,7 @@ const normalizeEmailTrait = (raw) => {
|
|
|
80
80
|
return null;
|
|
81
81
|
}
|
|
82
82
|
const normalized = value.trim().toLowerCase();
|
|
83
|
-
return
|
|
83
|
+
return detectWholeValuePII(normalized) === "email" ? normalized : null;
|
|
84
84
|
};
|
|
85
85
|
const emailDomain = (email) => {
|
|
86
86
|
if (email === null) {
|
|
@@ -3,8 +3,8 @@ import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
|
3
3
|
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
4
4
|
import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
|
|
5
5
|
import { resolveBrowserTokenEndpoint, resolveCanonicalIngestEndpoint, } from "../exporter/client.js";
|
|
6
|
-
import { ObservationSourceEventExporter } from "../exporter/observation-source-event-exporter.js";
|
|
7
6
|
import { createFrontendResourceAttributes } from "../exporter/frontend-resource.js";
|
|
7
|
+
import { ObservationSourceEventExporter } from "../exporter/observation-source-event-exporter.js";
|
|
8
8
|
import { createCluebaseNetworkPropagator } from "../instrumentation/network.js";
|
|
9
9
|
import { getSessionCaptureEligibility } from "./sdk-event-pipeline.js";
|
|
10
10
|
const globalSnapshots = new WeakMap();
|
|
@@ -88,7 +88,9 @@ export const createCluebaseOtelPipeline = ({ resolved, getTransport, getContext,
|
|
|
88
88
|
propagator: createCluebaseNetworkPropagator(),
|
|
89
89
|
});
|
|
90
90
|
globalSnapshots.set(provider, {
|
|
91
|
-
installedTracer: getTracerDelegate(trace.getTracerProvider()) === provider
|
|
91
|
+
installedTracer: getTracerDelegate(trace.getTracerProvider()) === provider
|
|
92
|
+
? provider
|
|
93
|
+
: null,
|
|
92
94
|
installedContextManager: isNoopContextManager(contextBeforeRegister)
|
|
93
95
|
? readContextManager()
|
|
94
96
|
: null,
|
|
@@ -162,8 +164,7 @@ export const forceFlushCluebaseOtelProvider = async (provider, signal) => {
|
|
|
162
164
|
try {
|
|
163
165
|
await awaitWithAbort(provider.forceFlush(), signal);
|
|
164
166
|
}
|
|
165
|
-
catch {
|
|
166
|
-
}
|
|
167
|
+
catch { }
|
|
167
168
|
};
|
|
168
169
|
const awaitWithAbort = async (promise, signal) => {
|
|
169
170
|
if (!signal)
|
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
import { TraceManager } from "../context/trace.js";
|
|
2
|
-
import { type IngestClient } from "../exporter/client.js";
|
|
3
2
|
import type { BatchTransport } from "../exporter/batch.js";
|
|
3
|
+
import { type IngestClient } from "../exporter/client.js";
|
|
4
4
|
import type { InternalMetricsStore } from "../internal/metrics.js";
|
|
5
|
-
import type
|
|
5
|
+
import { type CluebaseOtelProvider } from "./sdk-otel-pipeline.js";
|
|
6
6
|
import type { CaptureModuleContext, CluebaseConsentState, CluebaseInternalLogger, RawCapturedEvent, ResolvedCluebaseConfig } from "./types.js";
|
|
7
|
+
type RuntimeSurfacesObservationContext = {
|
|
8
|
+
anonymousId: string | null;
|
|
9
|
+
userId: string | null;
|
|
10
|
+
organizationId?: string | null;
|
|
11
|
+
sessionId: string | null;
|
|
12
|
+
tabId: string | null;
|
|
13
|
+
};
|
|
14
|
+
type RuntimeSurfacesEventPipeline = {
|
|
15
|
+
getViewId: () => string | null;
|
|
16
|
+
getObservationContext: () => RuntimeSurfacesObservationContext;
|
|
17
|
+
};
|
|
7
18
|
type ProviderRuntimeSurfacesOptions = {
|
|
8
19
|
internalMetrics: InternalMetricsStore;
|
|
9
20
|
logger: CluebaseInternalLogger;
|
|
10
21
|
enqueueRawEvent: (event: RawCapturedEvent) => void;
|
|
11
|
-
|
|
22
|
+
eventPipeline: RuntimeSurfacesEventPipeline;
|
|
12
23
|
getConsent: () => CluebaseConsentState;
|
|
13
|
-
getObservationContext: () => {
|
|
14
|
-
anonymousId: string | null;
|
|
15
|
-
userId: string | null;
|
|
16
|
-
organizationId?: string | null;
|
|
17
|
-
sessionId: string | null;
|
|
18
|
-
tabId: string | null;
|
|
19
|
-
};
|
|
20
24
|
getTraceManager: () => TraceManager | null;
|
|
21
25
|
getTransport: () => BatchTransport | null;
|
|
22
26
|
getTracerProvider: () => CluebaseOtelProvider | null;
|
|
@@ -25,9 +29,8 @@ export declare class ProviderRuntimeSurfaces {
|
|
|
25
29
|
private readonly internalMetrics;
|
|
26
30
|
private readonly logger;
|
|
27
31
|
private readonly enqueueRawEvent;
|
|
28
|
-
private readonly
|
|
32
|
+
private readonly eventPipeline;
|
|
29
33
|
private readonly getConsent;
|
|
30
|
-
private readonly getObservationContext;
|
|
31
34
|
private readonly getTraceManager;
|
|
32
35
|
private readonly getTransport;
|
|
33
36
|
private readonly getTracerProvider;
|
|
@@ -9,6 +9,7 @@ import { startNavigationCapture } from "../instrumentation/navigation.js";
|
|
|
9
9
|
import { startNetworkCapture } from "../instrumentation/network.js";
|
|
10
10
|
import { startPageCapture } from "../instrumentation/page.js";
|
|
11
11
|
import { startPerformanceCapture } from "../instrumentation/performance.js";
|
|
12
|
+
import { forceFlushCluebaseOtelProvider, } from "./sdk-otel-pipeline.js";
|
|
12
13
|
const FLUSH_DEBOUNCE_MS = 200;
|
|
13
14
|
export class ProviderRuntimeSurfaces {
|
|
14
15
|
constructor(options) {
|
|
@@ -19,9 +20,8 @@ export class ProviderRuntimeSurfaces {
|
|
|
19
20
|
this.internalMetrics = options.internalMetrics;
|
|
20
21
|
this.logger = options.logger;
|
|
21
22
|
this.enqueueRawEvent = options.enqueueRawEvent;
|
|
22
|
-
this.
|
|
23
|
+
this.eventPipeline = options.eventPipeline;
|
|
23
24
|
this.getConsent = options.getConsent;
|
|
24
|
-
this.getObservationContext = options.getObservationContext;
|
|
25
25
|
this.getTraceManager = options.getTraceManager;
|
|
26
26
|
this.getTransport = options.getTransport;
|
|
27
27
|
this.getTracerProvider = options.getTracerProvider;
|
|
@@ -38,10 +38,9 @@ export class ProviderRuntimeSurfaces {
|
|
|
38
38
|
error_code: "capture_module_error",
|
|
39
39
|
});
|
|
40
40
|
},
|
|
41
|
-
getViewId: () => this.getViewId(),
|
|
41
|
+
getViewId: () => this.eventPipeline.getViewId(),
|
|
42
42
|
getConsent: () => this.getConsent(),
|
|
43
|
-
getObservationContext: () => this.getObservationContext(),
|
|
44
|
-
shouldIgnoreElement: () => false,
|
|
43
|
+
getObservationContext: () => this.eventPipeline.getObservationContext(),
|
|
45
44
|
startInteraction: (input) => {
|
|
46
45
|
return (this.getTraceManager()?.startInteraction(input) ??
|
|
47
46
|
input.interactionId ??
|
|
@@ -56,6 +55,7 @@ export class ProviderRuntimeSurfaces {
|
|
|
56
55
|
return;
|
|
57
56
|
this.flushDebounceTimer = setTimeout(() => {
|
|
58
57
|
this.flushDebounceTimer = null;
|
|
58
|
+
void forceFlushCluebaseOtelProvider(this.getTracerProvider());
|
|
59
59
|
const transport = this.getTransport();
|
|
60
60
|
if (!transport)
|
|
61
61
|
return;
|
|
@@ -177,12 +177,14 @@ export class ProviderRuntimeSurfaces {
|
|
|
177
177
|
}
|
|
178
178
|
handled = true;
|
|
179
179
|
onUnloadSessionEnd();
|
|
180
|
+
void forceFlushCluebaseOtelProvider(this.getTracerProvider());
|
|
180
181
|
this.getTransport()?.flushOnUnload();
|
|
181
182
|
};
|
|
182
183
|
const visibilityHandler = () => {
|
|
183
184
|
if (document.visibilityState !== "hidden") {
|
|
184
185
|
return;
|
|
185
186
|
}
|
|
187
|
+
void forceFlushCluebaseOtelProvider(this.getTracerProvider());
|
|
186
188
|
this.getTransport()?.flushOnUnload();
|
|
187
189
|
};
|
|
188
190
|
window.addEventListener("pagehide", handler, { capture: true });
|
|
@@ -210,8 +212,7 @@ export class ProviderRuntimeSurfaces {
|
|
|
210
212
|
try {
|
|
211
213
|
dispose();
|
|
212
214
|
}
|
|
213
|
-
catch {
|
|
214
|
-
}
|
|
215
|
+
catch { }
|
|
215
216
|
}
|
|
216
217
|
this.unloadHandlers = [];
|
|
217
218
|
}
|