@genn-inc/cluebase-frontend-sdk 0.0.6 → 0.0.9

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 (69) hide show
  1. package/dist/_generated/correlation-attribute-keys.d.ts +3 -0
  2. package/dist/_generated/correlation-attribute-keys.js +35 -0
  3. package/dist/_generated/diagnostic-codes.d.ts +4 -0
  4. package/dist/_generated/diagnostic-codes.js +48 -0
  5. package/dist/_generated/drop-reasons.d.ts +2 -0
  6. package/dist/_generated/drop-reasons.js +25 -0
  7. package/dist/_generated/ingest-routes.d.ts +5 -0
  8. package/dist/_generated/ingest-routes.js +12 -0
  9. package/dist/context/trace.js +2 -4
  10. package/dist/exporter/batch.d.ts +23 -26
  11. package/dist/exporter/batch.js +254 -289
  12. package/dist/exporter/browser-outbox-records.d.ts +7 -24
  13. package/dist/exporter/browser-outbox-records.js +6 -94
  14. package/dist/exporter/browser-outbox.d.ts +23 -28
  15. package/dist/exporter/browser-outbox.js +99 -211
  16. package/dist/exporter/client.d.ts +24 -34
  17. package/dist/exporter/client.js +107 -318
  18. package/dist/exporter/delivery-pipeline.d.ts +72 -0
  19. package/dist/exporter/delivery-pipeline.js +199 -0
  20. package/dist/exporter/delivery-response-contract.d.ts +5 -81
  21. package/dist/exporter/delivery-response-contract.js +4 -166
  22. package/dist/exporter/delivery-response.d.ts +8 -4
  23. package/dist/exporter/delivery-response.js +62 -133
  24. package/dist/exporter/diagnostics.d.ts +2 -1
  25. package/dist/exporter/diagnostics.js +1 -25
  26. package/dist/exporter/event-id.d.ts +1 -0
  27. package/dist/exporter/event-id.js +3 -0
  28. package/dist/exporter/observation-source-event-exporter.d.ts +1 -1
  29. package/dist/exporter/observation-source-event-exporter.js +2 -2
  30. package/dist/exporter/parameter-encryption.d.ts +10 -0
  31. package/dist/exporter/parameter-encryption.js +219 -0
  32. package/dist/exporter/parameter-snapshot.js +25 -3
  33. package/dist/exporter/sdk-core-delivery.d.ts +226 -0
  34. package/dist/exporter/sdk-core-delivery.js +97 -0
  35. package/dist/exporter/sdk-core-wasm.d.ts +24 -1
  36. package/dist/exporter/sdk-core-wasm.js +31 -3
  37. package/dist/exporter/source-event.d.ts +2 -0
  38. package/dist/exporter/source-event.js +17 -0
  39. package/dist/exporter/span-to-source-signal.d.ts +1 -1
  40. package/dist/exporter/span-to-source-signal.js +20 -140
  41. package/dist/index.d.ts +3 -3
  42. package/dist/index.js +5 -3
  43. package/dist/instrumentation/network.d.ts +1 -1
  44. package/dist/instrumentation/network.js +17 -0
  45. package/dist/provider/_version.d.ts +1 -1
  46. package/dist/provider/_version.js +1 -1
  47. package/dist/provider/config.js +16 -3
  48. package/dist/provider/constants.js +0 -1
  49. package/dist/provider/contracts.d.ts +2 -0
  50. package/dist/provider/contracts.js +2 -0
  51. package/dist/provider/lifecycle-event-dedup.d.ts +7 -0
  52. package/dist/provider/lifecycle-event-dedup.js +31 -0
  53. package/dist/provider/sdk-encryption-store.d.ts +7 -0
  54. package/dist/provider/sdk-encryption-store.js +15 -0
  55. package/dist/provider/sdk-event-pipeline.d.ts +1 -3
  56. package/dist/provider/sdk-event-pipeline.js +0 -15
  57. package/dist/provider/sdk-otel-pipeline.d.ts +2 -1
  58. package/dist/provider/sdk-otel-pipeline.js +2 -11
  59. package/dist/provider/sdk-runtime-surfaces.d.ts +3 -2
  60. package/dist/provider/sdk-runtime-surfaces.js +7 -6
  61. package/dist/provider/sdk.js +88 -54
  62. package/dist/provider/types.d.ts +14 -19
  63. package/dist/wasm/pkg-web/cluebase_sdk_core_wasm.d.ts +18 -9
  64. package/dist/wasm/pkg-web/cluebase_sdk_core_wasm.js +170 -29
  65. package/dist/wasm/pkg-web/cluebase_sdk_core_wasm_bg.wasm +0 -0
  66. package/dist/wasm/pkg-web/cluebase_sdk_core_wasm_bg.wasm.d.ts +10 -9
  67. package/package.json +1 -1
  68. package/dist/provider/usage-governor.d.ts +0 -7
  69. package/dist/provider/usage-governor.js +0 -114
@@ -0,0 +1,226 @@
1
+ import type { DiagnosticCode } from "./diagnostics.js";
2
+ export type ConsentState = "unknown" | "granted" | "denied";
3
+ export type CollectionPermit = {
4
+ readonly generation: string;
5
+ readonly gate_id: string;
6
+ readonly permitted_at_start: boolean;
7
+ readonly capture_open_at_start: boolean;
8
+ };
9
+ export type TransportConfigInput = {
10
+ readonly surface: "frontend";
11
+ readonly endpoint: string;
12
+ readonly project_key: string;
13
+ readonly sdk_request?: string;
14
+ readonly sdk_version?: string;
15
+ readonly source_schema_version?: string;
16
+ };
17
+ export type QueueLimitsInput = {
18
+ readonly max_payload_bytes: number;
19
+ readonly max_buffer_bytes: number;
20
+ readonly max_buffer_events?: number;
21
+ };
22
+ export type DriverOptions = {
23
+ readonly transport: TransportConfigInput;
24
+ readonly queue_limits: QueueLimitsInput;
25
+ readonly request_timeout_ms?: number;
26
+ };
27
+ export type QueuedEvent = {
28
+ readonly event_key: string;
29
+ readonly byte_length: number;
30
+ readonly permit: CollectionPermit;
31
+ readonly diagnostic_canonical_bytes?: string;
32
+ };
33
+ export type RestoredEvent = {
34
+ readonly event_key: string;
35
+ readonly byte_length: number;
36
+ readonly diagnostic_canonical_bytes?: string;
37
+ };
38
+ export type DiagnosticEntry = {
39
+ readonly code: DiagnosticCode;
40
+ readonly count?: number;
41
+ readonly event_fingerprint?: string;
42
+ readonly batch_fingerprint?: string;
43
+ readonly http_status?: number;
44
+ readonly attempt?: number;
45
+ };
46
+ export type HostDiagnostic = {
47
+ readonly code: DiagnosticCode;
48
+ readonly count?: number;
49
+ readonly event_canonical_bytes?: string;
50
+ readonly batch_canonical_bytes?: string;
51
+ };
52
+ export type EnqueueReport = {
53
+ readonly retained: boolean;
54
+ readonly overflowed_event_keys: readonly string[];
55
+ readonly diagnostics: readonly DiagnosticEntry[];
56
+ };
57
+ export type FenceReport = {
58
+ readonly target_ordinal: number;
59
+ readonly oversized_event_keys: readonly string[];
60
+ readonly frame_admits_no_event: boolean;
61
+ readonly diagnostics: readonly DiagnosticEntry[];
62
+ };
63
+ export type RestoreReport = {
64
+ readonly adopted: boolean;
65
+ readonly event_keys: readonly string[];
66
+ readonly batch_id?: number;
67
+ };
68
+ export type ReleaseReport = {
69
+ readonly events: readonly QueuedEvent[];
70
+ };
71
+ export type QueueDepthReport = {
72
+ readonly draft_count: number;
73
+ readonly draft_body_bytes: number;
74
+ readonly pending_bytes: number;
75
+ };
76
+ export type ConsentReport = {
77
+ readonly consent: ConsentState;
78
+ readonly purged_event_keys: readonly string[];
79
+ readonly diagnostics: readonly DiagnosticEntry[];
80
+ };
81
+ export type TransportHeader = {
82
+ readonly name: string;
83
+ readonly value: string;
84
+ };
85
+ export type TransportRequest = {
86
+ readonly attempt_id: number;
87
+ readonly target: "ingest_batch" | "browser_token";
88
+ readonly method: string;
89
+ readonly url: string;
90
+ readonly headers: readonly TransportHeader[];
91
+ readonly body: string;
92
+ readonly timeout_ms: number;
93
+ readonly max_response_body_bytes: number;
94
+ readonly follow_redirects: boolean;
95
+ readonly keepalive: boolean;
96
+ readonly omit_credentials: boolean;
97
+ readonly suppress_instrumentation: boolean;
98
+ };
99
+ export type TransportAbortCause = "consent_revoked" | "deadline" | "shutdown";
100
+ export type TransportOutcome = {
101
+ readonly kind: "response";
102
+ readonly status: number;
103
+ readonly body: string;
104
+ readonly body_over_limit?: boolean;
105
+ } | {
106
+ readonly kind: "timeout";
107
+ } | {
108
+ readonly kind: "network_error";
109
+ } | {
110
+ readonly kind: "aborted";
111
+ readonly cause: TransportAbortCause;
112
+ };
113
+ export type DeliveryStep = {
114
+ readonly kind: "idle";
115
+ } | {
116
+ readonly kind: "send";
117
+ readonly attempt_id: number;
118
+ readonly batch_id: number;
119
+ readonly attempt: number;
120
+ readonly event_keys: readonly string[];
121
+ readonly timeout_ms: number;
122
+ } | {
123
+ readonly kind: "wait_retry";
124
+ readonly resume_at_ms: number;
125
+ readonly delay_ms: number;
126
+ } | {
127
+ readonly kind: "blocked";
128
+ readonly reason: "awaiting_result" | "consent_revoked" | "deadline_exceeded" | "retries_exhausted";
129
+ };
130
+ export type DeliveryProgress = {
131
+ readonly kind: "ignored";
132
+ } | {
133
+ readonly kind: "settled";
134
+ readonly batch_id: number;
135
+ readonly settled_event_keys: readonly string[];
136
+ readonly dropped_event_keys: readonly string[];
137
+ } | {
138
+ readonly kind: "split_scheduled";
139
+ readonly batch_id: number;
140
+ } | {
141
+ readonly kind: "dropped_oversized";
142
+ readonly batch_id: number;
143
+ readonly event_key: string;
144
+ } | {
145
+ readonly kind: "retry_scheduled";
146
+ readonly batch_id: number;
147
+ readonly delay_ms: number;
148
+ readonly resume_at_ms: number;
149
+ } | {
150
+ readonly kind: "retries_exhausted";
151
+ readonly batch_id: number;
152
+ } | {
153
+ readonly kind: "consent_revoked";
154
+ };
155
+ export type DroppedEventDetail = {
156
+ readonly event_index: number;
157
+ readonly reason: string;
158
+ readonly source_event_payload_fingerprint: string | null;
159
+ };
160
+ export type IngestVerdict = {
161
+ readonly accepted: boolean;
162
+ readonly observation_ingest_batch_id?: string;
163
+ readonly status?: string;
164
+ readonly reason?: string;
165
+ readonly retry_strategy?: string;
166
+ readonly submitted_event_count?: number;
167
+ readonly accepted_event_count?: number;
168
+ readonly dropped_event_count?: number;
169
+ readonly dropped_events?: readonly DroppedEventDetail[];
170
+ readonly duplicate?: boolean;
171
+ readonly error?: string;
172
+ readonly limits?: Readonly<Record<string, number>>;
173
+ };
174
+ export type DeliveryDecision = {
175
+ readonly outcome: string;
176
+ readonly action: "none" | "retry_same_batch" | "split_batch";
177
+ readonly result: IngestVerdict;
178
+ readonly response_contract: "valid" | "invalid" | "not_applicable";
179
+ readonly diagnostic_codes: readonly DiagnosticCode[];
180
+ readonly http_status?: number | null;
181
+ readonly used_status_fallback?: boolean;
182
+ readonly dropped_indexes?: readonly number[];
183
+ readonly privacy_policy?: unknown;
184
+ readonly sdk_encryption?: unknown;
185
+ readonly directive_unreadable?: boolean;
186
+ };
187
+ export type DeliveryReport = {
188
+ readonly progress: DeliveryProgress;
189
+ readonly decision?: DeliveryDecision;
190
+ readonly diagnostics: readonly DiagnosticEntry[];
191
+ };
192
+ export type ResponseEvaluation = {
193
+ readonly kind: "decision";
194
+ readonly decision: DeliveryDecision;
195
+ } | {
196
+ readonly kind: "consent_revoked";
197
+ };
198
+ export declare const openConsent: (consent: ConsentState) => number;
199
+ export declare const readConsentState: (consentHandle: number) => ConsentState;
200
+ export declare const setConsent: (consentHandle: number, consent: ConsentState) => ConsentReport;
201
+ export declare const openDriver: (options: DriverOptions, consentHandle: number) => number;
202
+ export declare const closeDriver: (handle: number) => boolean;
203
+ export declare const capturePermit: (handle: number) => CollectionPermit;
204
+ export declare const permitsCapture: (handle: number, permit: CollectionPermit) => boolean;
205
+ export declare const rebindPermit: (handle: number, permit: CollectionPermit) => CollectionPermit;
206
+ export declare const enqueueEvent: (handle: number, event: QueuedEvent) => EnqueueReport;
207
+ export declare const adoptRestoredBatch: (handle: number, ordinal: number, events: readonly RestoredEvent[]) => RestoreReport;
208
+ export declare const releaseDrafts: (handle: number) => ReleaseReport;
209
+ export declare const queueDepth: (handle: number, envelopeOverheadBytes: number) => QueueDepthReport;
210
+ export declare const fenceQueue: (handle: number, envelopeOverheadBytes: number) => FenceReport;
211
+ export declare const planNextStep: (handle: number, nowMs: number, deadlineMs?: number) => DeliveryStep;
212
+ export declare const attemptRequest: (handle: number, input: {
213
+ readonly attemptId: number;
214
+ readonly body: string;
215
+ readonly timeoutMs: number;
216
+ readonly batchCanonicalBytes?: string;
217
+ }) => TransportRequest;
218
+ export declare const applyTransportResult: (handle: number, attemptId: number, outcome: TransportOutcome, nowMs: number) => DeliveryReport;
219
+ export declare const evaluateResponse: (outcome: TransportOutcome, localSubmittedEventCount: number) => ResponseEvaluation;
220
+ export declare const setBrowserToken: (handle: number, token: string | null) => boolean;
221
+ export declare const beginClose: (handle: number) => void;
222
+ export declare const pendingEventCount: (handle: number) => number;
223
+ export declare const diagnosticsSnapshot: (handle: number) => Readonly<Record<string, unknown>>;
224
+ export declare const recordHostDiagnostic: (handle: number, diagnostic: HostDiagnostic) => void;
225
+ export declare const eventFingerprint: (handle: number, canonicalBytes: string) => string;
226
+ export declare const batchFingerprint: (handle: number, canonicalBytes: string) => string;
@@ -0,0 +1,97 @@
1
+ import { deliveryCommandJson } from "./sdk-core-wasm.js";
2
+ const call = (request) => JSON.parse(deliveryCommandJson(JSON.stringify(request)));
3
+ export const openConsent = (consent) => call({ command: "open_consent", consent })
4
+ .consent;
5
+ export const readConsentState = (consentHandle) => call({
6
+ handle: consentHandle,
7
+ command: "consent_state",
8
+ }).consent;
9
+ export const setConsent = (consentHandle, consent) => call({
10
+ handle: consentHandle,
11
+ command: "set_consent",
12
+ consent,
13
+ });
14
+ export const openDriver = (options, consentHandle) => call({
15
+ command: "open",
16
+ options,
17
+ consent: consentHandle,
18
+ }).handle;
19
+ export const closeDriver = (handle) => call({ handle, command: "close" }).closed;
20
+ export const capturePermit = (handle) => call({ handle, command: "capture_permit" });
21
+ export const permitsCapture = (handle, permit) => call({
22
+ handle,
23
+ command: "permits_capture",
24
+ permit,
25
+ }).permitted;
26
+ export const rebindPermit = (handle, permit) => call({ handle, command: "rebind_permit", permit });
27
+ export const enqueueEvent = (handle, event) => call({ handle, command: "enqueue", event });
28
+ export const adoptRestoredBatch = (handle, ordinal, events) => call({
29
+ handle,
30
+ command: "adopt_restored_batch",
31
+ ordinal,
32
+ events,
33
+ });
34
+ export const releaseDrafts = (handle) => call({ handle, command: "release_drafts" });
35
+ export const queueDepth = (handle, envelopeOverheadBytes) => call({
36
+ handle,
37
+ command: "queue_depth",
38
+ envelope_overhead_bytes: envelopeOverheadBytes,
39
+ });
40
+ export const fenceQueue = (handle, envelopeOverheadBytes) => call({
41
+ handle,
42
+ command: "fence",
43
+ envelope_overhead_bytes: envelopeOverheadBytes,
44
+ });
45
+ export const planNextStep = (handle, nowMs, deadlineMs) => call({
46
+ handle,
47
+ command: "plan_next_step",
48
+ now_ms: nowMs,
49
+ ...(deadlineMs === undefined ? {} : { deadline_ms: deadlineMs }),
50
+ });
51
+ export const attemptRequest = (handle, input) => call({
52
+ handle,
53
+ command: "attempt_request",
54
+ attempt_id: input.attemptId,
55
+ body: input.body,
56
+ timeout_ms: input.timeoutMs,
57
+ ...(input.batchCanonicalBytes === undefined
58
+ ? {}
59
+ : { batch_canonical_bytes: input.batchCanonicalBytes }),
60
+ });
61
+ export const applyTransportResult = (handle, attemptId, outcome, nowMs) => call({
62
+ handle,
63
+ command: "apply_result",
64
+ result: { attempt_id: attemptId, outcome },
65
+ now_ms: nowMs,
66
+ });
67
+ export const evaluateResponse = (outcome, localSubmittedEventCount) => call({
68
+ command: "evaluate_response",
69
+ outcome,
70
+ local_submitted_event_count: localSubmittedEventCount,
71
+ surface: "frontend",
72
+ });
73
+ export const setBrowserToken = (handle, token) => call({ handle, command: "set_browser_token", token });
74
+ export const beginClose = (handle) => {
75
+ call({ handle, command: "begin_close" });
76
+ };
77
+ export const pendingEventCount = (handle) => call({
78
+ handle,
79
+ command: "pending_event_count",
80
+ }).pending_event_count;
81
+ export const diagnosticsSnapshot = (handle) => call({
82
+ handle,
83
+ command: "diagnostics_snapshot",
84
+ });
85
+ export const recordHostDiagnostic = (handle, diagnostic) => {
86
+ call({ handle, command: "record_diagnostic", diagnostic });
87
+ };
88
+ export const eventFingerprint = (handle, canonicalBytes) => call({
89
+ handle,
90
+ command: "event_fingerprint",
91
+ canonical_bytes: canonicalBytes,
92
+ }).fingerprint;
93
+ export const batchFingerprint = (handle, canonicalBytes) => call({
94
+ handle,
95
+ command: "batch_fingerprint",
96
+ canonical_bytes: canonicalBytes,
97
+ }).fingerprint;
@@ -2,4 +2,27 @@ import init from "../wasm/pkg-web/cluebase_sdk_core_wasm.js";
2
2
  export type SdkCoreWasmInitInput = Parameters<typeof init>[0];
3
3
  export declare const initSdkCoreWasm: (input?: SdkCoreWasmInitInput) => Promise<void>;
4
4
  export declare const isSdkCoreWasmReady: () => boolean;
5
- export declare const spanToSdkSignalJson: (spanJson: string, optionsJson: string) => string;
5
+ export declare const spanToObservationSourceEventJson: (spanJson: string, optionsJson: string) => string;
6
+ export declare const deliveryCommandJson: (requestJson: string) => string;
7
+ export type ParameterCaptureDecision = "plain" | "encrypt" | "mask";
8
+ export type ParameterValueFact = {
9
+ readonly type: "boolean";
10
+ } | {
11
+ readonly type: "number";
12
+ readonly number: number;
13
+ } | {
14
+ readonly type: "text";
15
+ readonly text: string;
16
+ } | {
17
+ readonly type: "other";
18
+ };
19
+ export type ParameterCaptureRequestEntry = {
20
+ readonly key: string;
21
+ readonly value: ParameterValueFact;
22
+ };
23
+ export declare const decideParameterCaptures: (entries: readonly ParameterCaptureRequestEntry[]) => readonly ParameterCaptureDecision[];
24
+ export type IngestEndpointDescription = {
25
+ readonly ingestBatchUrl: string;
26
+ readonly browserTokenUrl: string;
27
+ };
28
+ export declare const describeIngestEndpoint: (endpoint: string) => Promise<IngestEndpointDescription>;
@@ -1,4 +1,4 @@
1
- import init, { spanToSdkSignalJson as wasmSpanToSdkSignalJson, } from "../wasm/pkg-web/cluebase_sdk_core_wasm.js";
1
+ import init, { deliveryCommandJson as wasmDeliveryCommandJson, parameterCaptureDecisionsJson as wasmParameterCaptureDecisionsJson, spanToObservationSourceEventJson as wasmSpanToObservationSourceEventJson, } from "../wasm/pkg-web/cluebase_sdk_core_wasm.js";
2
2
  let readyPromise = null;
3
3
  let ready = false;
4
4
  export const initSdkCoreWasm = (input) => {
@@ -10,9 +10,37 @@ export const initSdkCoreWasm = (input) => {
10
10
  return readyPromise;
11
11
  };
12
12
  export const isSdkCoreWasmReady = () => ready;
13
- export const spanToSdkSignalJson = (spanJson, optionsJson) => {
13
+ const requireReady = () => {
14
14
  if (!ready) {
15
15
  throw new Error("cluebase sdk core wasm is not initialized; call initSdkCoreWasm() first");
16
16
  }
17
- return wasmSpanToSdkSignalJson(spanJson, optionsJson);
17
+ };
18
+ export const spanToObservationSourceEventJson = (spanJson, optionsJson) => {
19
+ requireReady();
20
+ return wasmSpanToObservationSourceEventJson(spanJson, optionsJson);
21
+ };
22
+ export const deliveryCommandJson = (requestJson) => {
23
+ requireReady();
24
+ return wasmDeliveryCommandJson(requestJson);
25
+ };
26
+ export const decideParameterCaptures = (entries) => {
27
+ if (entries.length === 0)
28
+ return [];
29
+ requireReady();
30
+ const reply = JSON.parse(wasmParameterCaptureDecisionsJson(JSON.stringify({ entries })));
31
+ return reply.decisions;
32
+ };
33
+ export const describeIngestEndpoint = async (endpoint) => {
34
+ if (!isSdkCoreWasmReady()) {
35
+ await initSdkCoreWasm();
36
+ }
37
+ const described = JSON.parse(deliveryCommandJson(JSON.stringify({
38
+ command: "describe_endpoint",
39
+ endpoint,
40
+ surface: "frontend",
41
+ })));
42
+ return {
43
+ ingestBatchUrl: described.ingest_batch_url,
44
+ browserTokenUrl: described.browser_token_url,
45
+ };
18
46
  };
@@ -1,6 +1,7 @@
1
1
  import type { CanonicalJsonValue, CluebasePrivacySummary, CluebaseSubjectProfile, SdkCollectionMode } from "../provider/types.js";
2
2
  export declare const SDK_SOURCE_SIGNAL_EVENT_NAME = "sdk_source_signal_observed";
3
3
  export declare const SDK_SOURCE_SIGNAL_EVENT_TYPE = "sdk_source_signal";
4
+ export declare const SOURCE_SCHEMA_VERSION = 1;
4
5
  export type BrowserSdkSignalContext = {
5
6
  producer_id: string | null;
6
7
  anonymous_id: string | null;
@@ -99,4 +100,5 @@ export type BrowserSourceSignalEvent = {
99
100
  sdk_signal: BrowserSdkSignal;
100
101
  };
101
102
  export type BrowserSourceEvent = BrowserSourceSignalEvent;
103
+ export declare const withSourceEvidence: (event: BrowserSourceEvent) => BrowserSourceEvent;
102
104
  export declare function isBrowserSourceSignalEvent(event: unknown): event is BrowserSourceSignalEvent;
@@ -1,5 +1,22 @@
1
+ import { DEFAULT_FRONTEND_SOURCE_IDENTIFIER } from "../provider/constants.js";
1
2
  export const SDK_SOURCE_SIGNAL_EVENT_NAME = "sdk_source_signal_observed";
2
3
  export const SDK_SOURCE_SIGNAL_EVENT_TYPE = "sdk_source_signal";
4
+ export const SOURCE_SCHEMA_VERSION = 1;
5
+ export const withSourceEvidence = (event) => ({
6
+ ...event,
7
+ source_event_id: event.source_event_id ?? event.event_id,
8
+ source_schema_version: String(SOURCE_SCHEMA_VERSION),
9
+ source_event_type: SDK_SOURCE_SIGNAL_EVENT_TYPE,
10
+ source_event_kind: SDK_SOURCE_SIGNAL_EVENT_TYPE,
11
+ event_name: SDK_SOURCE_SIGNAL_EVENT_NAME,
12
+ event_category: "context",
13
+ producer_id: DEFAULT_FRONTEND_SOURCE_IDENTIFIER,
14
+ service_name: event.service_name ?? DEFAULT_FRONTEND_SOURCE_IDENTIFIER,
15
+ privacy_state: event.privacy_state ?? "masked",
16
+ privacy_level: event.privacy_level ?? "default_masked",
17
+ masking_state: event.masking_state ?? "masked",
18
+ allowed_value_keys: event.allowed_value_keys ?? [],
19
+ });
3
20
  const isReadableObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
4
21
  export function isBrowserSourceSignalEvent(event) {
5
22
  if (!isReadableObject(event)) {
@@ -1,6 +1,6 @@
1
1
  import type { ReadableSpan } from "@opentelemetry/sdk-trace-base";
2
2
  import type { CluebaseContext } from "../provider/types.js";
3
- import { type BrowserSourceSignalEvent } from "./source-event.js";
3
+ import type { BrowserSourceSignalEvent } from "./source-event.js";
4
4
  export interface SpanToSourceSignalOptions {
5
5
  context: CluebaseContext;
6
6
  deniedKeys?: readonly string[];
@@ -1,55 +1,6 @@
1
- import { CLUEBASE_CORRELATION_ATTRIBUTE_KEYS } from "../_generated/correlation-attribute-keys.js";
2
- import { nanosToIso, parentSpanId } from "../_generated/otel-span-conversion.js";
3
- import { ALWAYS_DENIED_KEYS, EVENT_SOURCE, EVENT_VERSION, SOURCE_SCHEMA_VERSION, } from "../provider/constants.js";
4
- import { generateEventId } from "./event-id.js";
5
- import { isDeniedKey } from "./sanitize.js";
6
- import { initSdkCoreWasm, isSdkCoreWasmReady, spanToSdkSignalJson, } from "./sdk-core-wasm.js";
7
- import { SDK_SOURCE_SIGNAL_EVENT_NAME, SDK_SOURCE_SIGNAL_EVENT_TYPE, } from "./source-event.js";
8
- function withCoreMasking(summary, masking) {
9
- const maskedValueCount = masking?.masked_value_count ?? 0;
10
- const maskedFields = masking?.masked_fields ?? [];
11
- if (!summary) {
12
- if (maskedValueCount === 0 && maskedFields.length === 0)
13
- return undefined;
14
- return {
15
- level: "masked",
16
- masked_value_count: maskedValueCount,
17
- dropped_field_count: 0,
18
- masked_fields: [...maskedFields].sort(),
19
- rule_set_version: null,
20
- };
21
- }
22
- return {
23
- ...summary,
24
- masked_value_count: summary.masked_value_count + maskedValueCount,
25
- masked_fields: [
26
- ...new Set([...summary.masked_fields, ...maskedFields]),
27
- ].sort(),
28
- };
29
- }
30
- function rawAttrString(attributes, key) {
31
- const value = attributes?.[key];
32
- return typeof value === "string" && value.trim().length > 0
33
- ? value.trim()
34
- : null;
35
- }
36
- function preFilterDeniedAttributes(attributes, deniedKeys) {
37
- const filtered = {};
38
- for (const [key, value] of Object.entries(attributes ?? {})) {
39
- if (!CLUEBASE_CORRELATION_ATTRIBUTE_KEYS.has(key) &&
40
- isDeniedKey(key, deniedKeys)) {
41
- continue;
42
- }
43
- filtered[key] = value;
44
- }
45
- return filtered;
46
- }
47
- function seedFallbackAttribute(attributesForCore, attributeKey, candidateKeys, fallback) {
48
- const alreadyPresent = candidateKeys.some((candidateKey) => rawAttrString(attributesForCore, candidateKey) !== null);
49
- if (alreadyPresent || !fallback)
50
- return;
51
- attributesForCore[attributeKey] = fallback;
52
- }
1
+ import { parentSpanId } from "../_generated/otel-span-conversion.js";
2
+ import { EVENT_SOURCE } from "../provider/constants.js";
3
+ import { initSdkCoreWasm, isSdkCoreWasmReady, spanToObservationSourceEventJson, } from "./sdk-core-wasm.js";
53
4
  function resourceAttributesInput(resource) {
54
5
  const input = resource;
55
6
  try {
@@ -59,19 +10,13 @@ function resourceAttributesInput(resource) {
59
10
  return {};
60
11
  }
61
12
  }
62
- const FIXED_CORE_DENIED_KEYS = Object.freeze([...new Set(ALWAYS_DENIED_KEYS)].sort());
63
- function coreDeniedKeys(deniedKeys) {
64
- if (deniedKeys.length === 0)
65
- return FIXED_CORE_DENIED_KEYS;
66
- return [...new Set([...FIXED_CORE_DENIED_KEYS, ...deniedKeys])].sort();
67
- }
68
13
  function instrumentationScopeName(span) {
69
14
  const instrumentation = span;
70
15
  return (instrumentation.instrumentationScope?.name ??
71
16
  instrumentation.instrumentationLibrary?.name ??
72
17
  null);
73
18
  }
74
- function contextRecord(context, overrides) {
19
+ function contextRecord(context) {
75
20
  return {
76
21
  producer_id: context.producer_id,
77
22
  anonymous_id: context.anonymous_id,
@@ -80,11 +25,11 @@ function contextRecord(context, overrides) {
80
25
  user_profile: context.user_profile,
81
26
  session_id: context.session_id,
82
27
  tab_id: context.tab_id,
83
- trace_id: overrides.trace_id,
84
- request_id: overrides.request_id,
85
- request_span_id: overrides.request_span_id,
86
- action_id: overrides.action_id,
87
- interaction_id: overrides.interaction_id,
28
+ trace_id: context.trace_id,
29
+ request_id: context.request_id,
30
+ request_span_id: context.request_span_id,
31
+ action_id: context.action_id,
32
+ interaction_id: context.interaction_id,
88
33
  correlation_id: context.correlation_id,
89
34
  sdk_collection_mode: context.sdk_collection_mode,
90
35
  frontend_release: context.frontend_release,
@@ -119,100 +64,35 @@ export async function spanToSourceSignalEvent(span, options) {
119
64
  await initSdkCoreWasm();
120
65
  }
121
66
  const context = options.context;
122
- const deniedKeys = options.deniedKeys ?? [];
123
67
  const spanContext = span.spanContext();
124
- const spanId = spanContext.spanId || null;
125
- const parentId = parentSpanId(span, span.attributes);
126
- const traceId = rawAttrString(span.attributes, "cluebase.trace_id") ??
127
- spanContext.traceId ??
128
- undefined;
129
- const attributesForCore = preFilterDeniedAttributes(span.attributes, deniedKeys);
130
- seedFallbackAttribute(attributesForCore, "cluebase.request_span_id", ["cluebase.request_span_id"], spanId ?? context.request_span_id);
131
- seedFallbackAttribute(attributesForCore, "cluebase.request_id", ["cluebase.request.id", "cluebase.request_id"], context.request_id);
132
- seedFallbackAttribute(attributesForCore, "cluebase.action_id", ["cluebase.action.id", "cluebase.action_id"], context.action_id ?? undefined);
133
- seedFallbackAttribute(attributesForCore, "cluebase.interaction_id", ["cluebase.interaction.id", "cluebase.interaction_id"], context.interaction_id);
134
68
  const corePayload = {
135
69
  name: span.name,
136
70
  kind: typeof span.kind === "number" ? span.kind : undefined,
137
- trace_id: traceId,
138
- span_id: spanId ?? undefined,
139
- parent_span_id: parentId ?? undefined,
71
+ trace_id: spanContext.traceId,
72
+ span_id: spanContext.spanId,
73
+ parent_span_id: parentSpanId(span, span.attributes),
140
74
  start_time: span.startTime,
141
75
  end_time: span.endTime,
142
76
  status: span.status
143
77
  ? { code: span.status.code ?? undefined, message: span.status.message }
144
78
  : undefined,
145
- attributes: attributesForCore,
79
+ attributes: span.attributes,
146
80
  events: (span.events ?? []).map((event) => ({
147
81
  name: event.name,
148
- attributes: preFilterDeniedAttributes(event.attributes, deniedKeys),
82
+ attributes: event.attributes ?? {},
149
83
  })),
150
84
  resource_attributes: resourceAttributesInput(span.resource),
151
- instrumentation_scope_name: instrumentationScopeName(span) ?? undefined,
85
+ instrumentation_scope_name: instrumentationScopeName(span),
152
86
  };
153
87
  const coreOptions = {
154
- denied_keys: coreDeniedKeys(deniedKeys),
88
+ denied_keys: options.deniedKeys ?? [],
155
89
  surface: "frontend",
156
- };
157
- const core = JSON.parse(spanToSdkSignalJson(JSON.stringify(corePayload), JSON.stringify(coreOptions)));
158
- const resolvedTraceId = core.trace_id ?? traceId ?? "";
159
- const eventId = generateEventId();
160
- return {
161
- event_id: eventId,
162
- source_event_id: eventId,
163
- source_schema_version: SOURCE_SCHEMA_VERSION,
164
- source_event_type: SDK_SOURCE_SIGNAL_EVENT_TYPE,
165
- source_event_kind: SDK_SOURCE_SIGNAL_EVENT_TYPE,
90
+ sdk_collection_mode: context.sdk_collection_mode,
166
91
  producer_id: context.producer_id,
167
92
  service_name: EVENT_SOURCE,
168
- event_name: SDK_SOURCE_SIGNAL_EVENT_NAME,
169
- event_category: "context",
170
- event_source: EVENT_SOURCE,
171
- event_version: EVENT_VERSION,
172
- occurred_at: nanosToIso(core.start_time_unix_nano),
173
- ingested_at: new Date().toISOString(),
174
- status: "observed",
175
- sdk_collection_mode: context.sdk_collection_mode,
176
- privacy_state: "masked",
177
- privacy_level: "default_masked",
178
- masking_state: "masked",
179
- allowed_value_keys: [],
180
- privacy_summary: withCoreMasking(context.privacy_summary, core.privacy_masking),
181
- trace_id: resolvedTraceId,
182
- span_id: core.span_id,
183
- parent_span_id: core.parent_span_id,
184
- request_id: core.request_id,
185
- request_span_id: core.request_span_id,
186
- action_id: core.action_id,
187
- interaction_id: core.interaction_id,
188
93
  frontend_release: context.frontend_release,
189
- backend_release: null,
190
- properties: {},
191
- metrics: {},
192
- sdk_signal: {
193
- surface: "frontend",
194
- kind: "span",
195
- name: span.name,
196
- span_kind: core.span_kind,
197
- trace_id: resolvedTraceId,
198
- span_id: core.span_id,
199
- parent_span_id: core.parent_span_id,
200
- start_time_unix_nano: core.start_time_unix_nano,
201
- end_time_unix_nano: core.end_time_unix_nano,
202
- status_code: core.status_code ?? null,
203
- status_message: core.status_message,
204
- instrumentation_scope_name: core.instrumentation_scope_name,
205
- attributes: core.attributes,
206
- events: core.events,
207
- resource: core.resource,
208
- raw_resource: core.raw_resource,
209
- context: contextRecord(context, {
210
- trace_id: resolvedTraceId,
211
- request_id: core.request_id,
212
- request_span_id: core.request_span_id,
213
- action_id: core.action_id,
214
- interaction_id: core.interaction_id,
215
- }),
216
- },
94
+ context: contextRecord(context),
95
+ context_privacy_summaries: [context.privacy_summary],
217
96
  };
97
+ return JSON.parse(spanToObservationSourceEventJson(JSON.stringify(corePayload), JSON.stringify(coreOptions)));
218
98
  }
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import type { InternalMetricsSnapshot } from "./internal/metrics.js";
2
- import type { CluebaseConsentState, CluebaseGroupTraits, CluebaseGroupType, CluebaseInitOptions, CluebaseSubjectTraits, CluebaseTrackMetrics, CluebaseTrackProperties } from "./provider/types.js";
2
+ import type { CluebaseConsentState, CluebaseGroupTraits, CluebaseGroupType, CluebaseInitOptions, CluebaseLinkInput, CluebaseSubjectTraits, CluebaseTrackMetrics, CluebaseTrackProperties } from "./provider/types.js";
3
3
  declare const cluebase: {
4
4
  init(options: CluebaseInitOptions): void;
5
5
  identify(userId: string, traits?: CluebaseSubjectTraits): void;
6
6
  group(groupType: CluebaseGroupType, groupKey: string, traits?: CluebaseGroupTraits): void;
7
+ link(input: CluebaseLinkInput): void;
7
8
  linkExternalAccount(namespace: string, externalId: string): void;
8
- logout(): void;
9
9
  reset(): void;
10
10
  track(eventName: string, properties?: CluebaseTrackProperties, metrics?: CluebaseTrackMetrics): void;
11
11
  setConsent(consent: CluebaseConsentState): void;
@@ -18,5 +18,5 @@ declare const cluebase: {
18
18
  export { EXPORT_RESULT_FAILED, EXPORT_RESULT_SUCCESS, ObservationSourceEventExporter, type ObservationSourceEventExporterOptions, } from "./exporter/observation-source-event-exporter.js";
19
19
  export { attachNextRouterIntegration } from "./integrations/next-router.js";
20
20
  export { attachReactRouterIntegration } from "./integrations/react-router.js";
21
- export type { CluebaseConsentState, CluebaseGroupTraits, CluebaseGroupType, CluebaseInitOptions, CluebaseSubjectTraits, CluebaseTrackMetrics, CluebaseTrackProperties, InternalMetricsSnapshot, };
21
+ export type { CluebaseConsentState, CluebaseGroupTraits, CluebaseGroupType, CluebaseInitOptions, CluebaseLinkInput, CluebaseSubjectTraits, CluebaseTrackMetrics, CluebaseTrackProperties, InternalMetricsSnapshot, };
22
22
  export default cluebase;