@genn-inc/cluebase-frontend-sdk 0.0.6 → 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 (62) hide show
  1. package/dist/_generated/correlation-attribute-keys.d.ts +3 -0
  2. package/dist/_generated/correlation-attribute-keys.js +34 -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 +247 -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 +105 -318
  18. package/dist/exporter/delivery-pipeline.d.ts +71 -0
  19. package/dist/exporter/delivery-pipeline.js +196 -0
  20. package/dist/exporter/delivery-response-contract.d.ts +1 -81
  21. package/dist/exporter/delivery-response-contract.js +0 -168
  22. package/dist/exporter/delivery-response.d.ts +7 -4
  23. package/dist/exporter/delivery-response.js +59 -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/sdk-core-delivery.d.ts +225 -0
  31. package/dist/exporter/sdk-core-delivery.js +97 -0
  32. package/dist/exporter/sdk-core-wasm.d.ts +7 -1
  33. package/dist/exporter/sdk-core-wasm.js +24 -3
  34. package/dist/exporter/source-event.d.ts +2 -0
  35. package/dist/exporter/source-event.js +17 -0
  36. package/dist/exporter/span-to-source-signal.d.ts +1 -1
  37. package/dist/exporter/span-to-source-signal.js +20 -140
  38. package/dist/index.d.ts +3 -3
  39. package/dist/index.js +5 -3
  40. package/dist/instrumentation/network.d.ts +1 -1
  41. package/dist/instrumentation/network.js +17 -0
  42. package/dist/provider/_version.d.ts +1 -1
  43. package/dist/provider/_version.js +1 -1
  44. package/dist/provider/config.js +16 -3
  45. package/dist/provider/constants.js +0 -1
  46. package/dist/provider/lifecycle-event-dedup.d.ts +7 -0
  47. package/dist/provider/lifecycle-event-dedup.js +31 -0
  48. package/dist/provider/sdk-event-pipeline.d.ts +1 -3
  49. package/dist/provider/sdk-event-pipeline.js +0 -15
  50. package/dist/provider/sdk-otel-pipeline.d.ts +2 -1
  51. package/dist/provider/sdk-otel-pipeline.js +2 -11
  52. package/dist/provider/sdk-runtime-surfaces.d.ts +3 -2
  53. package/dist/provider/sdk-runtime-surfaces.js +7 -6
  54. package/dist/provider/sdk.js +88 -54
  55. package/dist/provider/types.d.ts +12 -19
  56. package/dist/wasm/pkg-web/cluebase_sdk_core_wasm.d.ts +12 -9
  57. package/dist/wasm/pkg-web/cluebase_sdk_core_wasm.js +117 -29
  58. package/dist/wasm/pkg-web/cluebase_sdk_core_wasm_bg.wasm +0 -0
  59. package/dist/wasm/pkg-web/cluebase_sdk_core_wasm_bg.wasm.d.ts +8 -9
  60. package/package.json +1 -1
  61. package/dist/provider/usage-governor.d.ts +0 -7
  62. package/dist/provider/usage-governor.js +0 -114
@@ -1,143 +1,69 @@
1
- import { parseGovernorFeedbackEnvelope } from "../provider/usage-governor.js";
2
- import { observationSourceEventBatchResponseSchema, } from "./delivery-response-contract.js";
3
- export const BROWSER_REJECTION_STRATEGIES = {
4
- invalid_request: "stop_and_report",
5
- unsupported_source_schema_version: "stop_and_report",
6
- missing_project_key: "stop_and_report",
7
- project_key_mismatch: "stop_and_report",
8
- unresolved_project_or_environment: "stop_and_report",
9
- missing_browser_token: "stop_and_report",
10
- invalid_browser_token: "stop_and_report",
11
- expired_browser_token: "retry_same_batch",
12
- stale_browser_token: "retry_same_batch",
13
- origin_not_allowed: "stop_and_report",
14
- authorization_denied: "stop_and_report",
15
- idempotency_conflict: "mark_failed",
16
- batch_too_large: "split_batch",
17
- all_events_dropped: "drop_event",
18
- usage_governor_denied: "retry_same_batch",
19
- all_events_rejected_by_ingest_controls: "retry_same_batch",
20
- route_throttled: "retry_same_batch",
21
- raw_ingest_produce_failed: "retry_same_batch",
22
- raw_ingest_ack_unknown: "retry_same_batch",
23
- raw_ingest_receipt_metadata_failed: "retry_same_batch",
24
- raw_ingest_receipt_unprocessable: "stop_and_report",
25
- ingest_config_changed_retryable: "retry_same_batch",
26
- usage_meter_unavailable: "retry_same_batch",
1
+ import { evaluateResponse, } from "./sdk-core-delivery.js";
2
+ const RETRY_STRATEGY_BY_OUTCOME = {
3
+ accepted_complete: null,
4
+ accepted_filtered: null,
5
+ accepted_partial: null,
6
+ retry_same_batch: "retry_same_batch",
7
+ split_batch: "split_batch",
8
+ terminal_stop: "stop_and_report",
9
+ terminal_failed: "mark_failed",
10
+ terminal_drop: "drop_event",
11
+ invalid_response: "retry_same_batch",
27
12
  };
28
- export const evaluateBrowserDeliveryResponse = (input) => {
29
- const decoded = decode(input.bodyText);
30
- if (decoded !== null && hasAcceptedDiscriminator(decoded)) {
31
- const parsed = observationSourceEventBatchResponseSchema.safeParse(decoded);
32
- if (parsed.success &&
33
- submittedCountMatches(parsed.data, input.localSubmittedCount) &&
34
- hasPublicDropDetails(parsed.data)) {
35
- if (parsed.data.accepted) {
36
- if (input.status < 200 || input.status >= 300) {
37
- return invalidResponse();
38
- }
39
- const partial = parsed.data.dropped_event_count > 0;
40
- const diagnosticCodes = [];
41
- if (partial) {
42
- diagnosticCodes.push(parsed.data.dropped_events.length > 0
43
- ? "intentional_drop"
44
- : "duplicate_drop_details_unavailable");
45
- }
46
- if (input.status !== 202) {
47
- diagnosticCodes.push("non_canonical_success_status");
48
- }
49
- return {
50
- ok: true,
51
- retryStrategy: null,
52
- privacyPolicy: parsed.data.privacy_policy,
53
- governor: parseGovernorFeedbackEnvelope(parsed.data),
54
- observationIngestBatchId: parsed.data.observation_ingest_batch_id,
55
- droppedEventCount: parsed.data.dropped_event_count,
56
- acceptedEventCount: parsed.data.accepted_event_count,
57
- acceptedPartial: partial,
58
- reason: partial ? "accepted_partial" : null,
59
- diagnosticCodes,
60
- };
61
- }
62
- if (BROWSER_REJECTION_STRATEGIES[parsed.data.reason] !==
63
- parsed.data.retry_strategy) {
64
- return invalidResponse();
65
- }
66
- return {
67
- ok: false,
68
- retryStrategy: parsed.data.retry_strategy,
69
- governor: null,
70
- droppedEventCount: parsed.data.dropped_event_count ?? 0,
71
- acceptedEventCount: parsed.data.accepted_event_count ?? 0,
72
- acceptedPartial: false,
73
- reason: parsed.data.reason,
74
- diagnosticCodes: [],
75
- };
76
- }
77
- return invalidResponse();
13
+ const ACCEPTED_OUTCOMES = new Set([
14
+ "accepted_complete",
15
+ "accepted_filtered",
16
+ "accepted_partial",
17
+ ]);
18
+ export const evaluateBrowserDeliveryResponse = (input) => translate({ kind: "response", status: input.status, body: input.bodyText }, input.localSubmittedCount);
19
+ export const evaluateBrowserTransportOutcome = (outcome) => translate(outcome, 1);
20
+ export const classifyIngestHttpStatus = (status) => translate({ kind: "response", status, body: "" }, 1).retryStrategy ??
21
+ "retry_same_batch";
22
+ const translate = (outcome, localSubmittedCount) => {
23
+ const evaluation = evaluateResponse(outcome, localSubmittedCount);
24
+ if (evaluation.kind !== "decision") {
25
+ return consentRevoked();
78
26
  }
79
- return fallback(input.status);
27
+ return project(evaluation.decision);
80
28
  };
81
- const submittedCountMatches = (value, localSubmittedCount) => value.submitted_event_count === undefined ||
82
- value.submitted_event_count === localSubmittedCount;
83
- const hasPublicDropDetails = (value) => (value.dropped_events ?? []).every((detail) => detail.source_event_payload_fingerprint === null);
84
- const decode = (bodyText) => {
85
- if (!bodyText.trim())
86
- return null;
87
- try {
88
- return JSON.parse(bodyText);
89
- }
90
- catch {
91
- return null;
92
- }
29
+ const project = (decision) => {
30
+ const accepted = ACCEPTED_OUTCOMES.has(decision.outcome);
31
+ const batchId = decision.result.observation_ingest_batch_id;
32
+ return {
33
+ ok: accepted,
34
+ retryStrategy: accepted
35
+ ? null
36
+ : (RETRY_STRATEGY_BY_OUTCOME[decision.outcome] ?? "retry_same_batch"),
37
+ ...(decision.privacy_policy === undefined
38
+ ? {}
39
+ : { privacyPolicy: decision.privacy_policy }),
40
+ directiveUnreadable: decision.directive_unreadable === true,
41
+ ...(batchId === undefined ? {} : { observationIngestBatchId: batchId }),
42
+ droppedEventCount: decision.result.dropped_event_count ?? 0,
43
+ acceptedEventCount: decision.result.accepted_event_count ?? 0,
44
+ acceptedPartial: decision.outcome === "accepted_partial",
45
+ acceptedFiltered: decision.outcome === "accepted_filtered",
46
+ reason: reasonOf(decision),
47
+ diagnosticCodes: decision.diagnostic_codes,
48
+ };
93
49
  };
94
- const hasAcceptedDiscriminator = (value) => value !== null &&
95
- typeof value === "object" &&
96
- !Array.isArray(value) &&
97
- Object.prototype.hasOwnProperty.call(value, "accepted");
98
- const invalidResponse = () => ({
99
- ok: false,
100
- retryStrategy: "retry_same_batch",
101
- governor: null,
102
- droppedEventCount: 0,
103
- acceptedEventCount: 0,
104
- acceptedPartial: false,
105
- reason: "invalid_response",
106
- diagnosticCodes: ["invalid_response"],
107
- });
108
- export const classifyIngestHttpStatus = (status) => {
109
- if (status >= 200 && status < 300)
110
- return "retry_same_batch";
111
- if (status === 409)
112
- return "mark_failed";
113
- if (status === 413)
114
- return "split_batch";
115
- if ([400, 401, 403, 404, 405, 406, 410, 415].includes(status)) {
116
- return "stop_and_report";
117
- }
118
- return "retry_same_batch";
50
+ const reasonOf = (decision) => {
51
+ if (decision.outcome === "accepted_complete")
52
+ return null;
53
+ if (decision.outcome === "accepted_partial")
54
+ return "accepted_partial";
55
+ if (decision.outcome === "accepted_filtered")
56
+ return "accepted_filtered";
57
+ return decision.result.reason ?? decision.outcome;
119
58
  };
120
- const RETRYABLE_CLIENT_STATUSES = [408, 422, 425, 429];
121
- const fallback = (status) => ({
59
+ const consentRevoked = () => ({
122
60
  ok: false,
123
- retryStrategy: classifyIngestHttpStatus(status),
124
- governor: null,
61
+ retryStrategy: "drop_event",
62
+ directiveUnreadable: false,
125
63
  droppedEventCount: 0,
126
64
  acceptedEventCount: 0,
127
65
  acceptedPartial: false,
128
- reason: `http_${status}`,
129
- diagnosticCodes: fallbackDiagnosticCodes(status),
66
+ acceptedFiltered: false,
67
+ reason: "consent_denied",
68
+ diagnosticCodes: [],
130
69
  });
131
- const fallbackDiagnosticCodes = (status) => {
132
- if (classifyIngestHttpStatus(status) !== "retry_same_batch")
133
- return [];
134
- if (status >= 200 && status <= 299)
135
- return ["invalid_response"];
136
- if (status >= 300 && status <= 399)
137
- return ["protocol_redirect"];
138
- if (status >= 500 && status <= 599)
139
- return [];
140
- if (RETRYABLE_CLIENT_STATUSES.includes(status))
141
- return ["invalid_response"];
142
- return ["unknown_status"];
143
- };
@@ -1,5 +1,6 @@
1
+ import { type DiagnosticCode } from "../_generated/diagnostic-codes.js";
1
2
  import type { IngestRetryStrategy } from "./delivery-response-contract.js";
2
- export type DiagnosticCode = "batch_split" | "consent_persistence_failed" | "consent_purge" | "consent_purge_failed" | "consent_state_unavailable" | "durable_outbox_unavailable" | "duplicate_drop_details_unavailable" | "event_oversize" | "intentional_drop" | "invalid_response" | "legacy_pre_revocation_event_blocked" | "legacy_bridge_capacity_reached" | "legacy_writer_detected" | "non_canonical_success_status" | "outbox_key_collision" | "outbox_record_corrupt" | "protocol_redirect" | "queue_overflow" | "retry_exhausted" | "retry_scheduled" | "shutdown_failure" | "terminal_rejection" | "unknown_status";
3
+ export type { DiagnosticCode };
3
4
  export type DiagnosticLifecycle = "open" | "closing" | "closing_failed" | "closed";
4
5
  export type DiagnosticFlushOutcome = "clean" | "failed" | null;
5
6
  export type DiagnosticCloseOutcome = "clean" | "retryable_failure" | "terminal_failure" | null;
@@ -1,31 +1,7 @@
1
1
  import { hmac } from "@noble/hashes/hmac";
2
2
  import { sha256 } from "@noble/hashes/sha256";
3
3
  import { bytesToHex, randomBytes, utf8ToBytes } from "@noble/hashes/utils";
4
- const CODES = [
5
- "batch_split",
6
- "consent_persistence_failed",
7
- "consent_purge",
8
- "consent_purge_failed",
9
- "consent_state_unavailable",
10
- "durable_outbox_unavailable",
11
- "duplicate_drop_details_unavailable",
12
- "event_oversize",
13
- "intentional_drop",
14
- "invalid_response",
15
- "legacy_pre_revocation_event_blocked",
16
- "legacy_bridge_capacity_reached",
17
- "legacy_writer_detected",
18
- "non_canonical_success_status",
19
- "outbox_key_collision",
20
- "outbox_record_corrupt",
21
- "protocol_redirect",
22
- "queue_overflow",
23
- "retry_exhausted",
24
- "retry_scheduled",
25
- "shutdown_failure",
26
- "terminal_rejection",
27
- "unknown_status",
28
- ];
4
+ import { DIAGNOSTIC_CODES as CODES, } from "../_generated/diagnostic-codes.js";
29
5
  const CODE_SET = new Set(CODES);
30
6
  const STRATEGIES = new Set([
31
7
  "split_batch",
@@ -1,6 +1,7 @@
1
1
  export declare const generateMonotonicId: (prefix?: string) => string;
2
2
  export declare const generateTraceId: () => string;
3
3
  export declare const generateEventId: () => string;
4
+ export declare const generateBatchId: () => string;
4
5
  export declare const generateAnonymousId: () => string;
5
6
  export declare const generateSessionId: () => string;
6
7
  export declare const generateTabId: () => string;
@@ -49,6 +49,9 @@ export const generateTraceId = () => {
49
49
  export const generateEventId = () => {
50
50
  return generateMonotonicId();
51
51
  };
52
+ export const generateBatchId = () => {
53
+ return toHex(randomBytes(16));
54
+ };
52
55
  export const generateAnonymousId = () => {
53
56
  return generateMonotonicId("anon");
54
57
  };
@@ -1,7 +1,7 @@
1
1
  import { type ExportResult, ExportResultCode } from "@opentelemetry/core";
2
2
  import type { ReadableSpan, SpanExporter } from "@opentelemetry/sdk-trace-base";
3
3
  import type { CluebaseContext } from "../provider/types.js";
4
- import type { CollectionPermit } from "./browser-outbox.js";
4
+ import type { CollectionPermit } from "./sdk-core-delivery.js";
5
5
  import type { BrowserSourceEvent, BrowserSourceSignalEvent } from "./source-event.js";
6
6
  export declare const EXPORT_RESULT_SUCCESS: ExportResultCode;
7
7
  export declare const EXPORT_RESULT_FAILED: ExportResultCode;
@@ -15,7 +15,7 @@ export class ObservationSourceEventExporter {
15
15
  this.shutdownImpl = options.shutdownImpl;
16
16
  this.getContext = options.getContext;
17
17
  this.deniedKeys = options.deniedKeys ?? [];
18
- this.ignoreUrlPrefixes = (options.ignoreUrlPrefixes ?? []).filter((p) => typeof p === "string" && p.length > 0);
18
+ this.ignoreUrlPrefixes = options.ignoreUrlPrefixes ?? [];
19
19
  }
20
20
  ignoreSpanReason(span) {
21
21
  const spanFields = span;
@@ -30,7 +30,7 @@ export class ObservationSourceEventExporter {
30
30
  ? fullUrl
31
31
  : undefined;
32
32
  if (url &&
33
- this.ignoreUrlPrefixes.some((prefix) => url.startsWith(prefix))) {
33
+ this.ignoreUrlPrefixes.some((prefix) => prefix.length > 0 && url.startsWith(prefix))) {
34
34
  return "loop";
35
35
  }
36
36
  if (!Object.keys(attrs).some((key) => key.startsWith("cluebase."))) {
@@ -0,0 +1,225 @@
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 directive_unreadable?: boolean;
185
+ };
186
+ export type DeliveryReport = {
187
+ readonly progress: DeliveryProgress;
188
+ readonly decision?: DeliveryDecision;
189
+ readonly diagnostics: readonly DiagnosticEntry[];
190
+ };
191
+ export type ResponseEvaluation = {
192
+ readonly kind: "decision";
193
+ readonly decision: DeliveryDecision;
194
+ } | {
195
+ readonly kind: "consent_revoked";
196
+ };
197
+ export declare const openConsent: (consent: ConsentState) => number;
198
+ export declare const readConsentState: (consentHandle: number) => ConsentState;
199
+ export declare const setConsent: (consentHandle: number, consent: ConsentState) => ConsentReport;
200
+ export declare const openDriver: (options: DriverOptions, consentHandle: number) => number;
201
+ export declare const closeDriver: (handle: number) => boolean;
202
+ export declare const capturePermit: (handle: number) => CollectionPermit;
203
+ export declare const permitsCapture: (handle: number, permit: CollectionPermit) => boolean;
204
+ export declare const rebindPermit: (handle: number, permit: CollectionPermit) => CollectionPermit;
205
+ export declare const enqueueEvent: (handle: number, event: QueuedEvent) => EnqueueReport;
206
+ export declare const adoptRestoredBatch: (handle: number, ordinal: number, events: readonly RestoredEvent[]) => RestoreReport;
207
+ export declare const releaseDrafts: (handle: number) => ReleaseReport;
208
+ export declare const queueDepth: (handle: number, envelopeOverheadBytes: number) => QueueDepthReport;
209
+ export declare const fenceQueue: (handle: number, envelopeOverheadBytes: number) => FenceReport;
210
+ export declare const planNextStep: (handle: number, nowMs: number, deadlineMs?: number) => DeliveryStep;
211
+ export declare const attemptRequest: (handle: number, input: {
212
+ readonly attemptId: number;
213
+ readonly body: string;
214
+ readonly timeoutMs: number;
215
+ readonly batchCanonicalBytes?: string;
216
+ }) => TransportRequest;
217
+ export declare const applyTransportResult: (handle: number, attemptId: number, outcome: TransportOutcome, nowMs: number) => DeliveryReport;
218
+ export declare const evaluateResponse: (outcome: TransportOutcome, localSubmittedEventCount: number) => ResponseEvaluation;
219
+ export declare const setBrowserToken: (handle: number, token: string | null) => boolean;
220
+ export declare const beginClose: (handle: number) => void;
221
+ export declare const pendingEventCount: (handle: number) => number;
222
+ export declare const diagnosticsSnapshot: (handle: number) => Readonly<Record<string, unknown>>;
223
+ export declare const recordHostDiagnostic: (handle: number, diagnostic: HostDiagnostic) => void;
224
+ export declare const eventFingerprint: (handle: number, canonicalBytes: string) => string;
225
+ 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,10 @@ 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 IngestEndpointDescription = {
8
+ readonly ingestBatchUrl: string;
9
+ readonly browserTokenUrl: string;
10
+ };
11
+ 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, 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,30 @@ 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 describeIngestEndpoint = async (endpoint) => {
27
+ if (!isSdkCoreWasmReady()) {
28
+ await initSdkCoreWasm();
29
+ }
30
+ const described = JSON.parse(deliveryCommandJson(JSON.stringify({
31
+ command: "describe_endpoint",
32
+ endpoint,
33
+ surface: "frontend",
34
+ })));
35
+ return {
36
+ ingestBatchUrl: described.ingest_batch_url,
37
+ browserTokenUrl: described.browser_token_url,
38
+ };
18
39
  };