@genn-inc/cluebase-frontend-sdk 0.0.3 → 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/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 +16 -6
- 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-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 +67 -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 +62 -53
|
@@ -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";
|
|
@@ -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
|
}
|
package/dist/provider/sdk.js
CHANGED
|
@@ -7,15 +7,16 @@ import { TraceManager } from "../context/trace.js";
|
|
|
7
7
|
import { BatchTransport } from "../exporter/batch.js";
|
|
8
8
|
import { IngestClient } from "../exporter/client.js";
|
|
9
9
|
import { DiagnosticStore } from "../exporter/diagnostics.js";
|
|
10
|
-
import {
|
|
10
|
+
import { detectWholeValuePII, hashIdentityValue, isDeniedKey, } from "../exporter/sanitize.js";
|
|
11
|
+
import { initSdkCoreWasm } from "../exporter/sdk-core-wasm.js";
|
|
11
12
|
import { InternalMetricsStore } from "../internal/metrics.js";
|
|
12
13
|
import { isConfigValid, resolveConfig, validateConfig } from "./config.js";
|
|
13
14
|
import { DEFAULT_BATCH_FLUSH_THRESHOLD_BYTES, DEFAULT_CONSENT, DEFAULT_MAX_BATCH_QUEUE_BYTES, DEFAULT_MAX_BATCH_QUEUE_ITEMS, SDK_VERSION, } from "./constants.js";
|
|
14
|
-
import {
|
|
15
|
+
import { getSdkErrorDetails, reportSdkError } from "./sdk-error.js";
|
|
15
16
|
import { OFFICIAL_CUSTOM_EVENT_NAME, ProviderEventPipeline, readCurrentLifecycleUrl, } from "./sdk-event-pipeline.js";
|
|
17
|
+
import { isValidPublicCustomEventName, normalizeProfileAvatarUrl, normalizePublicIdentityId, toStringOrNull, } from "./sdk-input-normalization.js";
|
|
16
18
|
import { createCluebaseOtelPipeline, forceFlushCluebaseOtelProvider, shutdownCluebaseOtelProvider, shutdownCluebaseOtelProviderAsync, } from "./sdk-otel-pipeline.js";
|
|
17
19
|
import { ProviderRuntimeSurfaces } from "./sdk-runtime-surfaces.js";
|
|
18
|
-
import { getSdkErrorDetails, reportSdkError } from "./sdk-error.js";
|
|
19
20
|
const createNoopLogger = () => {
|
|
20
21
|
return {
|
|
21
22
|
debug: () => undefined,
|
|
@@ -58,9 +59,8 @@ class CluebaseBrowserSdkImpl {
|
|
|
58
59
|
enqueueRawEvent: (event) => {
|
|
59
60
|
this.enqueueRawEvent(event);
|
|
60
61
|
},
|
|
61
|
-
|
|
62
|
+
eventPipeline: this.eventPipeline,
|
|
62
63
|
getConsent: () => this.consent,
|
|
63
|
-
getObservationContext: () => this.eventPipeline.getObservationContext(),
|
|
64
64
|
getTraceManager: () => this.traceManager,
|
|
65
65
|
getTransport: () => this.transport,
|
|
66
66
|
getTracerProvider: () => this.tracerProvider,
|
|
@@ -72,6 +72,19 @@ class CluebaseBrowserSdkImpl {
|
|
|
72
72
|
getDiagnostics() {
|
|
73
73
|
return this.diagnostics.snapshot();
|
|
74
74
|
}
|
|
75
|
+
withActionContext(callback) {
|
|
76
|
+
let snapshot = null;
|
|
77
|
+
try {
|
|
78
|
+
snapshot = this.traceManager?.getCurrentActionContext() ?? null;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
reportSdkError("withActionContext", error);
|
|
82
|
+
}
|
|
83
|
+
if (!snapshot || !this.traceManager) {
|
|
84
|
+
return callback();
|
|
85
|
+
}
|
|
86
|
+
return this.traceManager.runWithActionContext(snapshot, callback);
|
|
87
|
+
}
|
|
75
88
|
init(options) {
|
|
76
89
|
try {
|
|
77
90
|
const resolved = resolveConfig(options);
|
|
@@ -89,7 +102,6 @@ class CluebaseBrowserSdkImpl {
|
|
|
89
102
|
localSetupCheckEndpoint: resolved.localSetupCheckEndpoint,
|
|
90
103
|
projectKey: resolved.projectKey,
|
|
91
104
|
producerId: resolved.producerId,
|
|
92
|
-
transportProfile: resolved.transportProfile,
|
|
93
105
|
autocapture: resolved.autocapture,
|
|
94
106
|
captureNetwork: resolved.captureNetwork,
|
|
95
107
|
captureErrors: resolved.captureErrors,
|
|
@@ -136,6 +148,7 @@ class CluebaseBrowserSdkImpl {
|
|
|
136
148
|
client: ingestClient,
|
|
137
149
|
enabled: effectiveBatch.enabled,
|
|
138
150
|
flushIntervalMs: effectiveBatch.flushIntervalMs,
|
|
151
|
+
maxSize: effectiveBatch.maxSize,
|
|
139
152
|
maxPayloadBytes: effectiveBatch.maxPayloadBytes,
|
|
140
153
|
flushThresholdBytes: DEFAULT_BATCH_FLUSH_THRESHOLD_BYTES,
|
|
141
154
|
queueMaxBytes: DEFAULT_MAX_BATCH_QUEUE_BYTES,
|
|
@@ -166,6 +179,9 @@ class CluebaseBrowserSdkImpl {
|
|
|
166
179
|
if (this.consent !== "denied") {
|
|
167
180
|
this.warmupBrowserToken(ingestClient);
|
|
168
181
|
}
|
|
182
|
+
void initSdkCoreWasm().catch((error) => {
|
|
183
|
+
reportSdkError("init sdk core wasm", error);
|
|
184
|
+
});
|
|
169
185
|
this.tracerProvider = createCluebaseOtelPipeline({
|
|
170
186
|
resolved,
|
|
171
187
|
getTransport: () => this.transport,
|
|
@@ -282,6 +298,44 @@ class CluebaseBrowserSdkImpl {
|
|
|
282
298
|
});
|
|
283
299
|
}
|
|
284
300
|
}
|
|
301
|
+
linkExternalAccount(namespace, externalId) {
|
|
302
|
+
if (!this.ensureInitializedForPublicCall("linkExternalAccount")) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (!this.organizationManager) {
|
|
306
|
+
this.warnPublicDrop("linkExternalAccount", "organization manager is unavailable");
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
const organizationId = this.organizationManager.getContext().organizationId;
|
|
311
|
+
if (!organizationId) {
|
|
312
|
+
this.warnPublicDrop("linkExternalAccount", "setOrganization must be called before linkExternalAccount");
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
const normalizedNamespace = normalizePublicIdentityId(namespace);
|
|
316
|
+
const normalizedExternalId = normalizePublicIdentityId(externalId);
|
|
317
|
+
if (!normalizedNamespace || !normalizedExternalId) {
|
|
318
|
+
this.warnPublicDrop("linkExternalAccount", "namespace and externalId must be non-empty strings");
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
const sanitizedExternalId = this.sanitizeIdentityValue("externalId", normalizedExternalId);
|
|
322
|
+
this.enqueueOfficialCustomEvent(OFFICIAL_CUSTOM_EVENT_NAME.externalAccountLinked, {
|
|
323
|
+
identity_action: "link_external_account",
|
|
324
|
+
external_namespace: normalizedNamespace,
|
|
325
|
+
}, false, {
|
|
326
|
+
link_type: "canonical_subject_to_external",
|
|
327
|
+
organization_id: organizationId,
|
|
328
|
+
external_namespace: normalizedNamespace,
|
|
329
|
+
external_id: sanitizedExternalId,
|
|
330
|
+
});
|
|
331
|
+
this.flushLifecycleEvents();
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
this.logger.error("linkExternalAccount failed", {
|
|
335
|
+
error_code: "sdk_operation_failed",
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
285
339
|
reset() {
|
|
286
340
|
if (!this.initialized) {
|
|
287
341
|
return;
|
|
@@ -425,8 +479,7 @@ class CluebaseBrowserSdkImpl {
|
|
|
425
479
|
});
|
|
426
480
|
}
|
|
427
481
|
}
|
|
428
|
-
catch {
|
|
429
|
-
}
|
|
482
|
+
catch { }
|
|
430
483
|
}
|
|
431
484
|
warnPublicFieldDrop(operation, field, _reason) {
|
|
432
485
|
this.logger.warn(`Cluebase Frontend SDK ${operation} field dropped`, {
|
|
@@ -436,11 +489,13 @@ class CluebaseBrowserSdkImpl {
|
|
|
436
489
|
try {
|
|
437
490
|
if (typeof console !== "undefined" &&
|
|
438
491
|
typeof console.warn === "function") {
|
|
439
|
-
console.warn(`[cluebase] ${operation} field dropped`, {
|
|
492
|
+
console.warn(`[cluebase] ${operation} field dropped`, {
|
|
493
|
+
field,
|
|
494
|
+
error_code: "sdk_field_drop",
|
|
495
|
+
});
|
|
440
496
|
}
|
|
441
497
|
}
|
|
442
|
-
catch {
|
|
443
|
-
}
|
|
498
|
+
catch { }
|
|
444
499
|
}
|
|
445
500
|
sanitizeIdentifyTraits(traits) {
|
|
446
501
|
if (!traits) {
|
|
@@ -551,7 +606,7 @@ class CluebaseBrowserSdkImpl {
|
|
|
551
606
|
const trimmed = raw.trim();
|
|
552
607
|
if (!trimmed)
|
|
553
608
|
return trimmed;
|
|
554
|
-
const piiCategory =
|
|
609
|
+
const piiCategory = detectWholeValuePII(trimmed);
|
|
555
610
|
if (piiCategory === null)
|
|
556
611
|
return trimmed;
|
|
557
612
|
const hashed = hashIdentityValue(trimmed);
|
package/dist/provider/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { InternalMetricsSnapshot } from "../internal/metrics.js";
|
|
2
1
|
import type { DiagnosticsSnapshot } from "../exporter/diagnostics.js";
|
|
2
|
+
import type { InternalMetricsSnapshot } from "../internal/metrics.js";
|
|
3
3
|
import type { FrontendEventName, ParameterCaptureMode, SdkCollectionMode } from "./contracts.js";
|
|
4
4
|
export type { FrontendEventCategory, FrontendEventName, FrontendEventStatus, ParameterCaptureMode, SdkCollectionMode, } from "./contracts.js";
|
|
5
5
|
export type CluebaseConsentState = "granted" | "denied" | "unknown";
|
|
@@ -19,7 +19,6 @@ export type CluebaseGroupTraits = {
|
|
|
19
19
|
} & {
|
|
20
20
|
[traitName: string]: CanonicalJsonInputValue;
|
|
21
21
|
};
|
|
22
|
-
export type TransportProfile = "standard";
|
|
23
22
|
export type RequestLifecycleOutcome = "completed" | "failed" | "aborted" | "unknown";
|
|
24
23
|
export type DiagnosticCollectionConfig = {
|
|
25
24
|
enabledCollectors?: string[];
|
|
@@ -67,7 +66,6 @@ export type ResolvedCluebaseConfig = {
|
|
|
67
66
|
localSetupCheckEndpoint: string | null;
|
|
68
67
|
projectKey: string;
|
|
69
68
|
producerId: string;
|
|
70
|
-
transportProfile: TransportProfile;
|
|
71
69
|
autocapture: boolean;
|
|
72
70
|
captureNetwork: boolean;
|
|
73
71
|
captureErrors: boolean;
|
|
@@ -112,6 +110,7 @@ export type CluebaseBrowserSdk = {
|
|
|
112
110
|
init(options: CluebaseInitOptions): void;
|
|
113
111
|
identify(userId: string, traits?: CluebaseSubjectTraits): void;
|
|
114
112
|
setOrganization(organizationId: string, traits?: CluebaseGroupTraits): void;
|
|
113
|
+
linkExternalAccount(namespace: string, externalId: string): void;
|
|
115
114
|
logout(): void;
|
|
116
115
|
reset(): void;
|
|
117
116
|
track(eventName: string, properties?: CluebaseTrackProperties, metrics?: CluebaseTrackMetrics): void;
|
|
@@ -120,6 +119,7 @@ export type CluebaseBrowserSdk = {
|
|
|
120
119
|
flush(): Promise<void>;
|
|
121
120
|
close(): Promise<void>;
|
|
122
121
|
getDiagnostics(): DiagnosticsSnapshot;
|
|
122
|
+
withActionContext<T>(callback: () => T): T;
|
|
123
123
|
};
|
|
124
124
|
export type { CostCapability } from "../_generated/usage-governor-tables.js";
|
|
125
125
|
import type { CostCapability } from "../_generated/usage-governor-tables.js";
|
|
@@ -149,12 +149,14 @@ export type ParameterSnapshotLeaf = {
|
|
|
149
149
|
export type ParameterSnapshotValue = ParameterSnapshotLeaf | ParameterSnapshotValue[] | {
|
|
150
150
|
[key: string]: ParameterSnapshotValue;
|
|
151
151
|
};
|
|
152
|
-
export type SdkIdentityLinkType = "anonymous_to_user" | "user_to_organization" | "logout";
|
|
152
|
+
export type SdkIdentityLinkType = "anonymous_to_user" | "user_to_organization" | "logout" | "canonical_subject_to_external";
|
|
153
153
|
export type SdkIdentityLinkHint = {
|
|
154
154
|
link_type: SdkIdentityLinkType;
|
|
155
155
|
anonymous_id?: string | null;
|
|
156
156
|
user_id?: string | null;
|
|
157
157
|
organization_id?: string | null;
|
|
158
|
+
external_namespace?: string | null;
|
|
159
|
+
external_id?: string | null;
|
|
158
160
|
};
|
|
159
161
|
export type CluebaseContext = {
|
|
160
162
|
producer_id: string | null;
|
|
@@ -267,7 +269,6 @@ export type CaptureModuleContext = {
|
|
|
267
269
|
sessionId: string | null;
|
|
268
270
|
tabId: string | null;
|
|
269
271
|
};
|
|
270
|
-
shouldIgnoreElement?: (element: Element) => boolean;
|
|
271
272
|
startInteraction?: (input: {
|
|
272
273
|
interactionId?: string | null;
|
|
273
274
|
interactionInitiator?: string | null;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
export function spanToSdkSignalJson(span_json: string, options_json: string): string;
|
|
5
|
+
|
|
6
|
+
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
|
7
|
+
|
|
8
|
+
export interface InitOutput {
|
|
9
|
+
readonly memory: WebAssembly.Memory;
|
|
10
|
+
readonly spanToSdkSignalJson: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
11
|
+
readonly __wbindgen_exn_store: (a: number) => void;
|
|
12
|
+
readonly __externref_table_alloc: () => number;
|
|
13
|
+
readonly __wbindgen_externrefs: WebAssembly.Table;
|
|
14
|
+
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
|
15
|
+
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
16
|
+
readonly __externref_table_dealloc: (a: number) => void;
|
|
17
|
+
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
|
|
18
|
+
readonly __wbindgen_start: () => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type SyncInitInput = BufferSource | WebAssembly.Module;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Instantiates the given `module`, which can either be bytes or
|
|
25
|
+
* a precompiled `WebAssembly.Module`.
|
|
26
|
+
*
|
|
27
|
+
* @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
|
|
28
|
+
*
|
|
29
|
+
* @returns {InitOutput}
|
|
30
|
+
*/
|
|
31
|
+
export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
|
|
35
|
+
* for everything else, calls `WebAssembly.instantiate` directly.
|
|
36
|
+
*
|
|
37
|
+
* @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
|
|
38
|
+
*
|
|
39
|
+
* @returns {Promise<InitOutput>}
|
|
40
|
+
*/
|
|
41
|
+
export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;
|