@genn-inc/cluebase-frontend-sdk 0.0.3 → 0.0.8
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/correlation-attribute-keys.d.ts +3 -0
- package/dist/_generated/correlation-attribute-keys.js +34 -0
- package/dist/_generated/diagnostic-codes.d.ts +4 -0
- package/dist/_generated/diagnostic-codes.js +48 -0
- package/dist/_generated/drop-reasons.d.ts +2 -0
- package/dist/_generated/drop-reasons.js +25 -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/ingest-routes.d.ts +5 -0
- package/dist/_generated/ingest-routes.js +12 -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 +4 -5
- package/dist/exporter/batch.d.ts +26 -27
- package/dist/exporter/batch.js +248 -294
- package/dist/exporter/browser-outbox-records.d.ts +8 -25
- package/dist/exporter/browser-outbox-records.js +21 -100
- package/dist/exporter/browser-outbox.d.ts +23 -25
- package/dist/exporter/browser-outbox.js +106 -181
- package/dist/exporter/client.d.ts +25 -35
- package/dist/exporter/client.js +107 -335
- package/dist/exporter/delivery-pipeline.d.ts +71 -0
- package/dist/exporter/delivery-pipeline.js +196 -0
- package/dist/exporter/delivery-response-contract.d.ts +1 -81
- package/dist/exporter/delivery-response-contract.js +0 -118
- package/dist/exporter/delivery-response.d.ts +9 -4
- package/dist/exporter/delivery-response.js +59 -122
- package/dist/exporter/diagnostics.d.ts +3 -2
- package/dist/exporter/diagnostics.js +5 -26
- package/dist/exporter/event-id.d.ts +1 -0
- package/dist/exporter/event-id.js +4 -3
- package/dist/exporter/frontend-resource.js +5 -4
- package/dist/exporter/observation-source-event-exporter.d.ts +2 -1
- package/dist/exporter/observation-source-event-exporter.js +7 -4
- 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-delivery.d.ts +225 -0
- package/dist/exporter/sdk-core-delivery.js +97 -0
- package/dist/exporter/sdk-core-wasm.d.ts +11 -0
- package/dist/exporter/sdk-core-wasm.js +39 -0
- package/dist/exporter/source-event.d.ts +3 -0
- package/dist/exporter/source-event.js +17 -0
- package/dist/exporter/span-to-source-signal.d.ts +2 -2
- package/dist/exporter/span-to-source-signal.js +45 -125
- package/dist/index.d.ts +5 -4
- package/dist/index.js +18 -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.d.ts +1 -1
- package/dist/instrumentation/network.js +24 -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 +16 -4
- package/dist/provider/constants.d.ts +1 -1
- package/dist/provider/constants.js +0 -2
- package/dist/provider/lifecycle-event-dedup.d.ts +7 -0
- package/dist/provider/lifecycle-event-dedup.js +31 -0
- package/dist/provider/sdk-event-pipeline.d.ts +3 -4
- package/dist/provider/sdk-event-pipeline.js +5 -17
- package/dist/provider/sdk-input-normalization.js +2 -2
- package/dist/provider/sdk-otel-pipeline.d.ts +2 -1
- package/dist/provider/sdk-otel-pipeline.js +7 -15
- package/dist/provider/sdk-runtime-surfaces.d.ts +17 -13
- package/dist/provider/sdk-runtime-surfaces.js +15 -13
- package/dist/provider/sdk.js +137 -48
- package/dist/provider/types.d.ts +18 -24
- package/dist/wasm/pkg-web/cluebase_sdk_core_wasm.d.ts +44 -0
- package/dist/wasm/pkg-web/cluebase_sdk_core_wasm.js +355 -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 +11 -0
- package/dist/wasm/pkg-web/package.json +15 -0
- package/package.json +62 -53
- package/dist/provider/usage-governor.d.ts +0 -7
- package/dist/provider/usage-governor.js +0 -114
|
@@ -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.8";
|
package/dist/provider/config.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CANONICAL_BROWSER_INGEST_PATH, CLUEBASE_API_V1_PREFIX, } from "../_generated/ingest-routes.js";
|
|
1
2
|
import { ALWAYS_DENIED_KEYS, DEFAULT_API_BASE_URL, DEFAULT_CONFIG, DEFAULT_FRONTEND_SOURCE_IDENTIFIER, MAX_DIAGNOSTIC_TTL_MS, } from "./constants.js";
|
|
2
3
|
import { SDK_COLLECTION_MODE } from "./contracts.js";
|
|
3
4
|
const normalizeKeyList = (keys) => {
|
|
@@ -74,6 +75,12 @@ export const parseSendableUrl = (value) => {
|
|
|
74
75
|
return null;
|
|
75
76
|
}
|
|
76
77
|
};
|
|
78
|
+
const isExtendableIngestPath = (pathname) => {
|
|
79
|
+
const path = pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
|
|
80
|
+
return (path === "" ||
|
|
81
|
+
path.endsWith(CLUEBASE_API_V1_PREFIX) ||
|
|
82
|
+
path.endsWith(CANONICAL_BROWSER_INGEST_PATH));
|
|
83
|
+
};
|
|
77
84
|
const normalizeConfiguredEndpoint = (value) => parseSendableUrl(value)?.toString() ?? "";
|
|
78
85
|
const normalizeTracePropagationOrigin = (value) => {
|
|
79
86
|
const trimmed = value.trim();
|
|
@@ -101,10 +108,8 @@ export const resolveConfig = (options) => {
|
|
|
101
108
|
return {
|
|
102
109
|
endpoint: originalEndpoint,
|
|
103
110
|
originalEndpoint,
|
|
104
|
-
localSetupCheckEndpoint: null,
|
|
105
111
|
projectKey: options.projectKey?.trim() ?? "",
|
|
106
112
|
producerId: DEFAULT_FRONTEND_SOURCE_IDENTIFIER,
|
|
107
|
-
transportProfile: DEFAULT_CONFIG.transportProfile,
|
|
108
113
|
autocapture: options.autocapture ?? DEFAULT_CONFIG.autocapture,
|
|
109
114
|
captureNetwork: options.captureNetwork ?? DEFAULT_CONFIG.captureNetwork,
|
|
110
115
|
captureErrors: options.captureErrors ?? DEFAULT_CONFIG.captureErrors,
|
|
@@ -139,8 +144,15 @@ export const resolveConfig = (options) => {
|
|
|
139
144
|
};
|
|
140
145
|
export const validateConfig = (config) => {
|
|
141
146
|
const issues = [];
|
|
142
|
-
|
|
143
|
-
|
|
147
|
+
const endpoint = parseSendableUrl(config.endpoint);
|
|
148
|
+
if (!endpoint) {
|
|
149
|
+
issues.push("endpoint must be an absolute http(s) url with a host, such as https://api.cluebase.io");
|
|
150
|
+
}
|
|
151
|
+
else if (endpoint.search || endpoint.hash) {
|
|
152
|
+
issues.push("endpoint must not carry a query string or fragment; pass the api base url only");
|
|
153
|
+
}
|
|
154
|
+
else if (!isExtendableIngestPath(endpoint.pathname)) {
|
|
155
|
+
issues.push(`endpoint path cannot be extended to ${CANONICAL_BROWSER_INGEST_PATH}; pass the api base url, the ${CLUEBASE_API_V1_PREFIX} base, or the canonical ingest path`);
|
|
144
156
|
}
|
|
145
157
|
if (!config.projectKey) {
|
|
146
158
|
issues.push("projectKey is required");
|
|
@@ -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";
|
|
@@ -41,10 +41,8 @@ export const MAX_SANITIZE_DEPTH = 20;
|
|
|
41
41
|
export const DEFAULT_CONFIG = {
|
|
42
42
|
endpoint: "",
|
|
43
43
|
originalEndpoint: "",
|
|
44
|
-
localSetupCheckEndpoint: null,
|
|
45
44
|
projectKey: "",
|
|
46
45
|
producerId: DEFAULT_FRONTEND_SOURCE_IDENTIFIER,
|
|
47
|
-
transportProfile: "standard",
|
|
48
46
|
autocapture: true,
|
|
49
47
|
captureNetwork: true,
|
|
50
48
|
captureErrors: true,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CanonicalJsonValue } from "./types.js";
|
|
2
|
+
export type LifecycleEventIdentity = Readonly<Record<string, CanonicalJsonValue>>;
|
|
3
|
+
export declare class LifecycleEventDedup {
|
|
4
|
+
private readonly seenAtMs;
|
|
5
|
+
shouldEmit(identity: LifecycleEventIdentity): boolean;
|
|
6
|
+
clear(): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { canonicalize } from "json-canonicalize";
|
|
2
|
+
const LIFECYCLE_DEDUPE_TTL_MS = 300000;
|
|
3
|
+
const LIFECYCLE_DEDUPE_MAX_ENTRIES = 4096;
|
|
4
|
+
export class LifecycleEventDedup {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.seenAtMs = new Map();
|
|
7
|
+
}
|
|
8
|
+
shouldEmit(identity) {
|
|
9
|
+
const key = canonicalize(identity);
|
|
10
|
+
const now = Date.now();
|
|
11
|
+
for (const [seenKey, seenAt] of this.seenAtMs) {
|
|
12
|
+
if (now - seenAt > LIFECYCLE_DEDUPE_TTL_MS) {
|
|
13
|
+
this.seenAtMs.delete(seenKey);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (this.seenAtMs.has(key)) {
|
|
17
|
+
this.seenAtMs.set(key, now);
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
if (this.seenAtMs.size >= LIFECYCLE_DEDUPE_MAX_ENTRIES) {
|
|
21
|
+
const oldest = [...this.seenAtMs.entries()].sort((left, right) => left[1] - right[1])[0]?.[0];
|
|
22
|
+
if (oldest !== undefined)
|
|
23
|
+
this.seenAtMs.delete(oldest);
|
|
24
|
+
}
|
|
25
|
+
this.seenAtMs.set(key, now);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
clear() {
|
|
29
|
+
this.seenAtMs.clear();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -1,16 +1,17 @@
|
|
|
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
|
-
import type { CluebaseConsentState, CluebaseContext, CluebaseInternalLogger,
|
|
9
|
+
import type { CluebaseConsentState, CluebaseContext, CluebaseInternalLogger, RawCapturedEvent, ResolvedCluebaseConfig } from "./types.js";
|
|
10
10
|
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>;
|
|
@@ -32,7 +33,6 @@ export declare const getSessionCaptureEligibility: (transport: BatchTransport) =
|
|
|
32
33
|
export declare class ProviderEventPipeline {
|
|
33
34
|
private runtime;
|
|
34
35
|
private currentViewId;
|
|
35
|
-
private usageGovernorFeedback;
|
|
36
36
|
private sessionCaptureEligible;
|
|
37
37
|
private unregisterSessionCaptureEligibility;
|
|
38
38
|
configure(runtime: ProviderEventPipelineRuntime): void;
|
|
@@ -47,7 +47,6 @@ export declare class ProviderEventPipeline {
|
|
|
47
47
|
tabId: string | null;
|
|
48
48
|
};
|
|
49
49
|
enqueueRawEvent(event: RawCapturedEvent, skipSessionTouch?: boolean): void;
|
|
50
|
-
applyUsageGovernorFeedback(feedback: IngestUsageGovernorFeedback | null | undefined): void;
|
|
51
50
|
refreshSessionCaptureEligibility(): void;
|
|
52
51
|
buildContext(event?: RawCapturedEvent): CluebaseContext;
|
|
53
52
|
private attachInteractionContext;
|
|
@@ -1,9 +1,8 @@
|
|
|
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";
|
|
@@ -23,6 +22,7 @@ export const OFFICIAL_CUSTOM_EVENT_NAME = {
|
|
|
23
22
|
identityIdentified: "identity_identified",
|
|
24
23
|
identityReset: "identity_reset",
|
|
25
24
|
organizationAssociated: "organization_associated",
|
|
25
|
+
externalAccountLinked: "external_account_linked",
|
|
26
26
|
sdkInitialized: "sdk_initialized",
|
|
27
27
|
};
|
|
28
28
|
const OFFICIAL_CUSTOM_EVENT_NAMES = new Set(Object.values(OFFICIAL_CUSTOM_EVENT_NAME));
|
|
@@ -75,7 +75,6 @@ export class ProviderEventPipeline {
|
|
|
75
75
|
constructor() {
|
|
76
76
|
this.runtime = null;
|
|
77
77
|
this.currentViewId = null;
|
|
78
|
-
this.usageGovernorFeedback = DEFAULT_INGEST_USAGE_GOVERNOR_FEEDBACK;
|
|
79
78
|
this.sessionCaptureEligible = true;
|
|
80
79
|
this.unregisterSessionCaptureEligibility = null;
|
|
81
80
|
}
|
|
@@ -85,14 +84,12 @@ export class ProviderEventPipeline {
|
|
|
85
84
|
this.unregisterSessionCaptureEligibility =
|
|
86
85
|
registerSessionCaptureEligibility(runtime.transport, () => this.sessionCaptureEligible);
|
|
87
86
|
this.currentViewId = generateViewId();
|
|
88
|
-
this.usageGovernorFeedback = DEFAULT_INGEST_USAGE_GOVERNOR_FEEDBACK;
|
|
89
87
|
this.refreshSessionCaptureEligibility();
|
|
90
88
|
}
|
|
91
89
|
clearRuntime() {
|
|
92
90
|
this.unregisterSessionCaptureEligibility?.();
|
|
93
91
|
this.unregisterSessionCaptureEligibility = null;
|
|
94
92
|
this.runtime = null;
|
|
95
|
-
this.usageGovernorFeedback = DEFAULT_INGEST_USAGE_GOVERNOR_FEEDBACK;
|
|
96
93
|
this.sessionCaptureEligible = true;
|
|
97
94
|
}
|
|
98
95
|
resetViewId() {
|
|
@@ -149,10 +146,6 @@ export class ProviderEventPipeline {
|
|
|
149
146
|
runtime.internalMetrics.increment("sampling_dropped_count", droppedCount);
|
|
150
147
|
}
|
|
151
148
|
}
|
|
152
|
-
if (isCostCapabilityDisabled(this.usageGovernorFeedback, "event_collection")) {
|
|
153
|
-
runtime.internalMetrics.increment("dropped_count");
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
149
|
const sourceSignal = rawEventToSourceSignalEvent(eventWithLocation, {
|
|
157
150
|
context,
|
|
158
151
|
resource: createFrontendResourceAttributes(runtime.config),
|
|
@@ -187,13 +180,6 @@ export class ProviderEventPipeline {
|
|
|
187
180
|
});
|
|
188
181
|
}
|
|
189
182
|
}
|
|
190
|
-
applyUsageGovernorFeedback(feedback) {
|
|
191
|
-
const nextFeedback = normalizeIngestUsageGovernorFeedback(feedback);
|
|
192
|
-
if (!hasUsageGovernorFeedbackChanged(this.usageGovernorFeedback, nextFeedback)) {
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
this.usageGovernorFeedback = nextFeedback;
|
|
196
|
-
}
|
|
197
183
|
refreshSessionCaptureEligibility() {
|
|
198
184
|
const config = this.runtime?.config;
|
|
199
185
|
if (!config) {
|
|
@@ -458,7 +444,9 @@ export class ProviderEventPipeline {
|
|
|
458
444
|
}
|
|
459
445
|
function sanitizeContextTraits(traits, deniedKeys, excludedKeys = new Set(), summary, pathPrefix = "", maskIdentityPii = true, preserveExplicitTraits = false) {
|
|
460
446
|
const sanitized = sanitizeObject(traits, deniedKeys, summary, pathPrefix, maskIdentityPii, preserveExplicitTraits);
|
|
461
|
-
if (typeof sanitized !== "object" ||
|
|
447
|
+
if (typeof sanitized !== "object" ||
|
|
448
|
+
sanitized === null ||
|
|
449
|
+
Array.isArray(sanitized)) {
|
|
462
450
|
return {};
|
|
463
451
|
}
|
|
464
452
|
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) {
|
|
@@ -6,8 +6,9 @@ type CreateCluebaseOtelPipelineOptions = {
|
|
|
6
6
|
resolved: ResolvedCluebaseConfig;
|
|
7
7
|
getTransport: () => BatchTransport | null;
|
|
8
8
|
getContext: () => CluebaseContext;
|
|
9
|
+
ignoreUrlPrefixes: readonly string[];
|
|
9
10
|
};
|
|
10
|
-
export declare const createCluebaseOtelPipeline: ({ resolved, getTransport, getContext, }: CreateCluebaseOtelPipelineOptions) => CluebaseOtelProvider | null;
|
|
11
|
+
export declare const createCluebaseOtelPipeline: ({ resolved, getTransport, getContext, ignoreUrlPrefixes, }: CreateCluebaseOtelPipelineOptions) => CluebaseOtelProvider | null;
|
|
11
12
|
export declare const shutdownCluebaseOtelProvider: (provider: CluebaseOtelProvider | null) => void;
|
|
12
13
|
export declare const shutdownCluebaseOtelProviderAsync: (provider: CluebaseOtelProvider | null) => Promise<void>;
|
|
13
14
|
export declare const forceFlushCluebaseOtelProvider: (provider: CluebaseOtelProvider | null, signal?: AbortSignal) => Promise<void>;
|
|
@@ -2,9 +2,8 @@ import { context, propagation, trace } from "@opentelemetry/api";
|
|
|
2
2
|
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
3
3
|
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
4
4
|
import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
|
|
5
|
-
import { resolveBrowserTokenEndpoint, resolveCanonicalIngestEndpoint, } from "../exporter/client.js";
|
|
6
|
-
import { ObservationSourceEventExporter } from "../exporter/observation-source-event-exporter.js";
|
|
7
5
|
import { createFrontendResourceAttributes } from "../exporter/frontend-resource.js";
|
|
6
|
+
import { ObservationSourceEventExporter } from "../exporter/observation-source-event-exporter.js";
|
|
8
7
|
import { createCluebaseNetworkPropagator } from "../instrumentation/network.js";
|
|
9
8
|
import { getSessionCaptureEligibility } from "./sdk-event-pipeline.js";
|
|
10
9
|
const globalSnapshots = new WeakMap();
|
|
@@ -16,7 +15,7 @@ const getTracerDelegate = (provider) => {
|
|
|
16
15
|
const candidate = provider;
|
|
17
16
|
return candidate.getDelegate?.();
|
|
18
17
|
};
|
|
19
|
-
export const createCluebaseOtelPipeline = ({ resolved, getTransport, getContext, }) => {
|
|
18
|
+
export const createCluebaseOtelPipeline = ({ resolved, getTransport, getContext, ignoreUrlPrefixes, }) => {
|
|
20
19
|
if (!getTransport()) {
|
|
21
20
|
return null;
|
|
22
21
|
}
|
|
@@ -58,15 +57,7 @@ export const createCluebaseOtelPipeline = ({ resolved, getTransport, getContext,
|
|
|
58
57
|
},
|
|
59
58
|
getContext,
|
|
60
59
|
deniedKeys: resolved.privacy?.deniedKeys ?? [],
|
|
61
|
-
ignoreUrlPrefixes
|
|
62
|
-
resolveCanonicalIngestEndpoint(resolved.endpoint),
|
|
63
|
-
resolveCanonicalIngestEndpoint(resolved.originalEndpoint),
|
|
64
|
-
resolveBrowserTokenEndpoint(resolved.endpoint),
|
|
65
|
-
resolveBrowserTokenEndpoint(resolved.originalEndpoint),
|
|
66
|
-
...(resolved.localSetupCheckEndpoint
|
|
67
|
-
? [resolved.localSetupCheckEndpoint]
|
|
68
|
-
: []),
|
|
69
|
-
].filter((url) => typeof url === "string" && url.length > 0),
|
|
60
|
+
ignoreUrlPrefixes,
|
|
70
61
|
});
|
|
71
62
|
const provider = new WebTracerProvider({
|
|
72
63
|
resource,
|
|
@@ -88,7 +79,9 @@ export const createCluebaseOtelPipeline = ({ resolved, getTransport, getContext,
|
|
|
88
79
|
propagator: createCluebaseNetworkPropagator(),
|
|
89
80
|
});
|
|
90
81
|
globalSnapshots.set(provider, {
|
|
91
|
-
installedTracer: getTracerDelegate(trace.getTracerProvider()) === provider
|
|
82
|
+
installedTracer: getTracerDelegate(trace.getTracerProvider()) === provider
|
|
83
|
+
? provider
|
|
84
|
+
: null,
|
|
92
85
|
installedContextManager: isNoopContextManager(contextBeforeRegister)
|
|
93
86
|
? readContextManager()
|
|
94
87
|
: null,
|
|
@@ -162,8 +155,7 @@ export const forceFlushCluebaseOtelProvider = async (provider, signal) => {
|
|
|
162
155
|
try {
|
|
163
156
|
await awaitWithAbort(provider.forceFlush(), signal);
|
|
164
157
|
}
|
|
165
|
-
catch {
|
|
166
|
-
}
|
|
158
|
+
catch { }
|
|
167
159
|
};
|
|
168
160
|
const awaitWithAbort = async (promise, signal) => {
|
|
169
161
|
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;
|
|
@@ -38,7 +41,8 @@ export declare class ProviderRuntimeSurfaces {
|
|
|
38
41
|
constructor(options: ProviderRuntimeSurfacesOptions);
|
|
39
42
|
buildCaptureContext(): CaptureModuleContext;
|
|
40
43
|
warmupBrowserToken(client: IngestClient): void;
|
|
41
|
-
|
|
44
|
+
refreshIgnoreUrlPrefixes(): void;
|
|
45
|
+
startCaptureModules(config: ResolvedCluebaseConfig, ignoreUrlPrefixes: readonly string[]): void;
|
|
42
46
|
stopCaptureModules(): void;
|
|
43
47
|
abortBrowserTokenWarmup(): void;
|
|
44
48
|
flushCaptureSummaries(reason: string): Promise<void>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { TraceManager } from "../context/trace.js";
|
|
2
|
-
import { resolveBrowserTokenEndpoint, resolveCanonicalIngestEndpoint, } from "../exporter/client.js";
|
|
3
2
|
import { startActionCapture } from "../instrumentation/action.js";
|
|
4
3
|
import { startDragCapture } from "../instrumentation/drag.js";
|
|
5
4
|
import { startErrorCapture } from "../instrumentation/error.js";
|
|
@@ -9,6 +8,7 @@ import { startNavigationCapture } from "../instrumentation/navigation.js";
|
|
|
9
8
|
import { startNetworkCapture } from "../instrumentation/network.js";
|
|
10
9
|
import { startPageCapture } from "../instrumentation/page.js";
|
|
11
10
|
import { startPerformanceCapture } from "../instrumentation/performance.js";
|
|
11
|
+
import { forceFlushCluebaseOtelProvider, } from "./sdk-otel-pipeline.js";
|
|
12
12
|
const FLUSH_DEBOUNCE_MS = 200;
|
|
13
13
|
export class ProviderRuntimeSurfaces {
|
|
14
14
|
constructor(options) {
|
|
@@ -19,9 +19,8 @@ export class ProviderRuntimeSurfaces {
|
|
|
19
19
|
this.internalMetrics = options.internalMetrics;
|
|
20
20
|
this.logger = options.logger;
|
|
21
21
|
this.enqueueRawEvent = options.enqueueRawEvent;
|
|
22
|
-
this.
|
|
22
|
+
this.eventPipeline = options.eventPipeline;
|
|
23
23
|
this.getConsent = options.getConsent;
|
|
24
|
-
this.getObservationContext = options.getObservationContext;
|
|
25
24
|
this.getTraceManager = options.getTraceManager;
|
|
26
25
|
this.getTransport = options.getTransport;
|
|
27
26
|
this.getTracerProvider = options.getTracerProvider;
|
|
@@ -38,10 +37,9 @@ export class ProviderRuntimeSurfaces {
|
|
|
38
37
|
error_code: "capture_module_error",
|
|
39
38
|
});
|
|
40
39
|
},
|
|
41
|
-
getViewId: () => this.getViewId(),
|
|
40
|
+
getViewId: () => this.eventPipeline.getViewId(),
|
|
42
41
|
getConsent: () => this.getConsent(),
|
|
43
|
-
getObservationContext: () => this.getObservationContext(),
|
|
44
|
-
shouldIgnoreElement: () => false,
|
|
42
|
+
getObservationContext: () => this.eventPipeline.getObservationContext(),
|
|
45
43
|
startInteraction: (input) => {
|
|
46
44
|
return (this.getTraceManager()?.startInteraction(input) ??
|
|
47
45
|
input.interactionId ??
|
|
@@ -56,6 +54,7 @@ export class ProviderRuntimeSurfaces {
|
|
|
56
54
|
return;
|
|
57
55
|
this.flushDebounceTimer = setTimeout(() => {
|
|
58
56
|
this.flushDebounceTimer = null;
|
|
57
|
+
void forceFlushCluebaseOtelProvider(this.getTracerProvider());
|
|
59
58
|
const transport = this.getTransport();
|
|
60
59
|
if (!transport)
|
|
61
60
|
return;
|
|
@@ -88,7 +87,12 @@ export class ProviderRuntimeSurfaces {
|
|
|
88
87
|
}
|
|
89
88
|
});
|
|
90
89
|
}
|
|
91
|
-
|
|
90
|
+
refreshIgnoreUrlPrefixes() {
|
|
91
|
+
for (const handle of this.captureHandles) {
|
|
92
|
+
handle.refreshIgnoreUrlPrefixes?.();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
startCaptureModules(config, ignoreUrlPrefixes) {
|
|
92
96
|
const captureContext = this.buildCaptureContext();
|
|
93
97
|
const handles = [];
|
|
94
98
|
handles.push(startInputCapture(captureContext));
|
|
@@ -101,10 +105,7 @@ export class ProviderRuntimeSurfaces {
|
|
|
101
105
|
handles.push(startNetworkCapture(captureContext, {
|
|
102
106
|
tracerProvider: this.getTracerProvider() ?? undefined,
|
|
103
107
|
deniedKeys: config.privacy.deniedKeys,
|
|
104
|
-
ignoreUrlPrefixes
|
|
105
|
-
resolveCanonicalIngestEndpoint(config.endpoint),
|
|
106
|
-
resolveBrowserTokenEndpoint(config.endpoint),
|
|
107
|
-
],
|
|
108
|
+
ignoreUrlPrefixes,
|
|
108
109
|
tracePropagationOrigins: config.network.tracePropagationOrigins,
|
|
109
110
|
createRequestTraceContext: (input) => {
|
|
110
111
|
const traceManager = this.getTraceManager();
|
|
@@ -177,12 +178,14 @@ export class ProviderRuntimeSurfaces {
|
|
|
177
178
|
}
|
|
178
179
|
handled = true;
|
|
179
180
|
onUnloadSessionEnd();
|
|
181
|
+
void forceFlushCluebaseOtelProvider(this.getTracerProvider());
|
|
180
182
|
this.getTransport()?.flushOnUnload();
|
|
181
183
|
};
|
|
182
184
|
const visibilityHandler = () => {
|
|
183
185
|
if (document.visibilityState !== "hidden") {
|
|
184
186
|
return;
|
|
185
187
|
}
|
|
188
|
+
void forceFlushCluebaseOtelProvider(this.getTracerProvider());
|
|
186
189
|
this.getTransport()?.flushOnUnload();
|
|
187
190
|
};
|
|
188
191
|
window.addEventListener("pagehide", handler, { capture: true });
|
|
@@ -210,8 +213,7 @@ export class ProviderRuntimeSurfaces {
|
|
|
210
213
|
try {
|
|
211
214
|
dispose();
|
|
212
215
|
}
|
|
213
|
-
catch {
|
|
214
|
-
}
|
|
216
|
+
catch { }
|
|
215
217
|
}
|
|
216
218
|
this.unloadHandlers = [];
|
|
217
219
|
}
|