@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.
Files changed (94) hide show
  1. package/README.md +21 -0
  2. package/dist/_generated/correlation-attribute-keys.d.ts +3 -0
  3. package/dist/_generated/correlation-attribute-keys.js +34 -0
  4. package/dist/_generated/diagnostic-codes.d.ts +4 -0
  5. package/dist/_generated/diagnostic-codes.js +48 -0
  6. package/dist/_generated/drop-reasons.d.ts +2 -0
  7. package/dist/_generated/drop-reasons.js +25 -0
  8. package/dist/_generated/{identity-pii-policy.d.ts → identity-pii-policy-text-contains.d.ts} +1 -1
  9. package/dist/_generated/{identity-pii-policy.js → identity-pii-policy-text-contains.js} +1 -1
  10. package/dist/_generated/identity-pii-policy-whole-value.d.ts +37 -0
  11. package/dist/_generated/identity-pii-policy-whole-value.js +106 -0
  12. package/dist/_generated/ingest-routes.d.ts +5 -0
  13. package/dist/_generated/ingest-routes.js +12 -0
  14. package/dist/_generated/otel-span-conversion.js +1 -9
  15. package/dist/context/browser-storage.js +1 -2
  16. package/dist/context/environment.js +5 -3
  17. package/dist/context/identity.js +5 -9
  18. package/dist/context/organization.d.ts +1 -1
  19. package/dist/context/organization.js +6 -8
  20. package/dist/context/session.js +6 -8
  21. package/dist/context/tab.js +8 -12
  22. package/dist/context/trace.js +4 -5
  23. package/dist/exporter/batch.d.ts +26 -27
  24. package/dist/exporter/batch.js +248 -294
  25. package/dist/exporter/browser-outbox-records.d.ts +8 -25
  26. package/dist/exporter/browser-outbox-records.js +21 -100
  27. package/dist/exporter/browser-outbox.d.ts +23 -25
  28. package/dist/exporter/browser-outbox.js +106 -181
  29. package/dist/exporter/client.d.ts +25 -35
  30. package/dist/exporter/client.js +107 -335
  31. package/dist/exporter/delivery-pipeline.d.ts +71 -0
  32. package/dist/exporter/delivery-pipeline.js +196 -0
  33. package/dist/exporter/delivery-response-contract.d.ts +1 -81
  34. package/dist/exporter/delivery-response-contract.js +0 -118
  35. package/dist/exporter/delivery-response.d.ts +9 -4
  36. package/dist/exporter/delivery-response.js +59 -122
  37. package/dist/exporter/diagnostics.d.ts +3 -2
  38. package/dist/exporter/diagnostics.js +5 -26
  39. package/dist/exporter/event-id.d.ts +1 -0
  40. package/dist/exporter/event-id.js +4 -3
  41. package/dist/exporter/frontend-resource.js +5 -4
  42. package/dist/exporter/observation-source-event-exporter.d.ts +2 -1
  43. package/dist/exporter/observation-source-event-exporter.js +7 -4
  44. package/dist/exporter/path-template.js +3 -1
  45. package/dist/exporter/raw-event-to-source-signal.js +4 -2
  46. package/dist/exporter/sanitize.d.ts +3 -2
  47. package/dist/exporter/sanitize.js +4 -3
  48. package/dist/exporter/sdk-core-delivery.d.ts +225 -0
  49. package/dist/exporter/sdk-core-delivery.js +97 -0
  50. package/dist/exporter/sdk-core-wasm.d.ts +11 -0
  51. package/dist/exporter/sdk-core-wasm.js +39 -0
  52. package/dist/exporter/source-event.d.ts +3 -0
  53. package/dist/exporter/source-event.js +17 -0
  54. package/dist/exporter/span-to-source-signal.d.ts +2 -2
  55. package/dist/exporter/span-to-source-signal.js +45 -125
  56. package/dist/index.d.ts +5 -4
  57. package/dist/index.js +18 -6
  58. package/dist/instrumentation/action.js +9 -8
  59. package/dist/instrumentation/dom.js +13 -21
  60. package/dist/instrumentation/drag.js +0 -3
  61. package/dist/instrumentation/error.js +3 -1
  62. package/dist/instrumentation/form.js +5 -6
  63. package/dist/instrumentation/input.js +4 -8
  64. package/dist/instrumentation/location.js +3 -1
  65. package/dist/instrumentation/network-request.js +1 -1
  66. package/dist/instrumentation/network-span-attributes.js +2 -2
  67. package/dist/instrumentation/network.d.ts +1 -1
  68. package/dist/instrumentation/network.js +24 -3
  69. package/dist/instrumentation/performance.js +1 -2
  70. package/dist/internal/internal-log.js +2 -1
  71. package/dist/provider/_version.d.ts +1 -1
  72. package/dist/provider/_version.js +1 -1
  73. package/dist/provider/config.js +16 -4
  74. package/dist/provider/constants.d.ts +1 -1
  75. package/dist/provider/constants.js +0 -2
  76. package/dist/provider/lifecycle-event-dedup.d.ts +7 -0
  77. package/dist/provider/lifecycle-event-dedup.js +31 -0
  78. package/dist/provider/sdk-event-pipeline.d.ts +3 -4
  79. package/dist/provider/sdk-event-pipeline.js +5 -17
  80. package/dist/provider/sdk-input-normalization.js +2 -2
  81. package/dist/provider/sdk-otel-pipeline.d.ts +2 -1
  82. package/dist/provider/sdk-otel-pipeline.js +7 -15
  83. package/dist/provider/sdk-runtime-surfaces.d.ts +17 -13
  84. package/dist/provider/sdk-runtime-surfaces.js +15 -13
  85. package/dist/provider/sdk.js +137 -48
  86. package/dist/provider/types.d.ts +18 -24
  87. package/dist/wasm/pkg-web/cluebase_sdk_core_wasm.d.ts +44 -0
  88. package/dist/wasm/pkg-web/cluebase_sdk_core_wasm.js +355 -0
  89. package/dist/wasm/pkg-web/cluebase_sdk_core_wasm_bg.wasm +0 -0
  90. package/dist/wasm/pkg-web/cluebase_sdk_core_wasm_bg.wasm.d.ts +11 -0
  91. package/dist/wasm/pkg-web/package.json +15 -0
  92. package/package.json +62 -53
  93. package/dist/provider/usage-governor.d.ts +0 -7
  94. 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 || typeof value === "boolean" ||
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.3";
1
+ export declare const SDK_VERSION: "0.0.8";
@@ -1,3 +1,3 @@
1
1
  // This file is generated by scripts/generate-version-module.mjs.
2
2
  // Do NOT edit by hand. The source of truth is `package.json` `version`.
3
- export const SDK_VERSION = "0.0.3";
3
+ export const SDK_VERSION = "0.0.8";
@@ -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
- if (!config.endpoint) {
143
- issues.push("endpoint is required");
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, IngestUsageGovernorFeedback, RawCapturedEvent, ResolvedCluebaseConfig } from "./types.js";
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" || sanitized === null || Array.isArray(sanitized)) {
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 { detectIdentityPII, hashIdentityValue } from "../exporter/sanitize.js";
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 detectIdentityPII(normalized) === "email" ? normalized : null;
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 ? provider : null,
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 { CluebaseOtelProvider } from "./sdk-otel-pipeline.js";
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
- getViewId: () => string | null;
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 getViewId;
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
- startCaptureModules(config: ResolvedCluebaseConfig): void;
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.getViewId = options.getViewId;
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
- startCaptureModules(config) {
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
  }