@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
@@ -1,30 +1,14 @@
1
- import type { BatchEnvelopeMetadata } from "./client.js";
2
1
  import type { CluebaseConsentState } from "../provider/types.js";
2
+ import type { BatchEnvelopeMetadata } from "./client.js";
3
3
  import { type BrowserSourceEvent } from "./source-event.js";
4
4
  export type StorageLike = Pick<Storage, "getItem" | "setItem" | "removeItem" | "key"> & {
5
5
  readonly length: number;
6
6
  };
7
- export type CollectionPermit = {
8
- readonly generation: string;
9
- readonly permittedAtStart: boolean;
10
- readonly captureOrdinal: number;
11
- };
12
7
  export type OwnedBrowserEvent = {
13
8
  readonly byteLength: number;
14
- readonly permit: CollectionPermit;
15
9
  readonly diagnosticCanonicalBytes: string;
16
10
  value(): BrowserSourceEvent;
17
11
  };
18
- export type PendingBrowserBatch = {
19
- readonly events: readonly OwnedBrowserEvent[];
20
- readonly metadata: BatchEnvelopeMetadata;
21
- readonly generation: string;
22
- readonly ordinal: number;
23
- readonly diagnosticCanonicalBytes: string;
24
- readonly recordKey: string;
25
- readonly ownerBatchId: string;
26
- readonly planOrdinal: number | null;
27
- };
28
12
  type RecordBase = {
29
13
  readonly storageVersion: 2;
30
14
  readonly projectHash: string;
@@ -98,9 +82,13 @@ export type PendingRecordLike = PendingRecord | SplitPlanRecord;
98
82
  type StoredWithoutChecksum = Omit<OutboxRecord, "checksumSha256">;
99
83
  export declare const UNANCHORED_COLLECTION_GENERATION = "__cluebase_unanchored_generation__";
100
84
  export declare const LEGACY_TOMBSTONE_LIMIT = 10000;
85
+ export type DraftPermit = {
86
+ readonly generation: string;
87
+ readonly permittedAtStart: boolean;
88
+ };
101
89
  export declare const createDraft: (input: {
102
90
  readonly event: BrowserSourceEvent;
103
- readonly permit: CollectionPermit;
91
+ readonly permit: DraftPermit;
104
92
  readonly projectHash: string;
105
93
  readonly createdAt: string;
106
94
  readonly origin?: "v2" | "legacy_v1";
@@ -122,13 +110,8 @@ export declare const createPending: (input: {
122
110
  readonly drafts: readonly DraftRecord[];
123
111
  readonly ordinal: number;
124
112
  }) => PendingRecord;
125
- export declare const toPendingBatch: (record: PendingRecordLike, recordKey: string, events: readonly OwnedStoredEvent[], metadata: BatchEnvelopeMetadata, planOrdinal: number | null) => PendingBrowserBatch;
126
- export declare const toHeadBatch: (record: PendingRecordLike, recordKey: string) => PendingBrowserBatch | null;
127
- export declare const splitPendingRecord: (record: PendingRecordLike, createMetadata: () => BatchEnvelopeMetadata) => SplitPlanRecord | null;
128
- export declare const largestFittingPrefix: (drafts: readonly DraftRecord[], maxBytes: number, metadata: BatchEnvelopeMetadata, measure: (events: readonly BrowserSourceEvent[], metadata: BatchEnvelopeMetadata) => number) => number;
129
- export declare const ownEvent: (event: BrowserSourceEvent, permit: CollectionPermit) => OwnedBrowserEvent | null;
130
- export declare const isOwnedEvent: (value: OwnedBrowserEvent | null) => value is OwnedBrowserEvent;
131
- export declare const recordBatchId: (batch: PendingBrowserBatch) => string;
113
+ export declare const sentByteLength: (event: BrowserSourceEvent) => number;
114
+ export declare const ownEvent: (event: BrowserSourceEvent) => OwnedBrowserEvent | null;
132
115
  export declare const withChecksum: <T extends StoredWithoutChecksum>(record: T) => T & {
133
116
  readonly checksumSha256: string;
134
117
  };
@@ -1,11 +1,11 @@
1
1
  import { sha256 } from "@noble/hashes/sha256";
2
2
  import { bytesToHex, utf8ToBytes } from "@noble/hashes/utils";
3
3
  import { canonicalize as canonicalJsonStringify } from "json-canonicalize";
4
- import { isBrowserSourceSignalEvent, } from "./source-event.js";
4
+ import { isBrowserSourceSignalEvent, withSourceEvidence, } from "./source-event.js";
5
5
  export const UNANCHORED_COLLECTION_GENERATION = "__cluebase_unanchored_generation__";
6
6
  export const LEGACY_TOMBSTONE_LIMIT = 10000;
7
7
  export const createDraft = (input) => {
8
- const owned = ownEvent(input.event, input.permit);
8
+ const owned = ownEvent(input.event);
9
9
  if (!owned)
10
10
  return null;
11
11
  const event = owned.value();
@@ -71,102 +71,16 @@ export const createPending = (input) => {
71
71
  ordinal: input.ordinal,
72
72
  });
73
73
  };
74
- export const toPendingBatch = (record, recordKey, events, metadata, planOrdinal) => {
75
- const owned = events
76
- .map((event) => ownEvent(event.event, {
77
- generation: record.collectionGeneration,
78
- permittedAtStart: event.permittedAtStart,
79
- captureOrdinal: 0,
80
- }))
81
- .filter(isOwnedEvent);
82
- return Object.freeze({
83
- events: Object.freeze(owned),
84
- metadata: Object.freeze({ ...metadata }),
85
- generation: record.collectionGeneration,
86
- ordinal: record.ordinal,
87
- diagnosticCanonicalBytes: canonicalJsonStringify({
88
- sourceType: "frontend_sdk",
89
- sourceSchemaVersion: "1",
90
- batchId: metadata.batch_id,
91
- idempotencyKey: metadata.idempotency_key,
92
- sentAt: metadata.sent_at,
93
- events: owned.map((event) => event.value()),
94
- }),
95
- recordKey,
96
- ownerBatchId: record.batchId,
97
- planOrdinal,
98
- });
99
- };
100
- export const toHeadBatch = (record, recordKey) => {
101
- if (record.kind === "pending") {
102
- return toPendingBatch(record, recordKey, record.events, {
103
- batch_id: record.batchId,
104
- idempotency_key: record.idempotencyKey,
105
- sent_at: record.sentAt,
106
- }, null);
107
- }
108
- const child = record.children[record.nextOrdinal];
109
- if (!child)
110
- return null;
111
- return toPendingBatch(record, recordKey, record.events.slice(child.start, child.end), child.metadata, record.nextOrdinal);
112
- };
113
- export const splitPendingRecord = (record, createMetadata) => {
114
- const child = record.kind === "pending"
115
- ? {
116
- start: 0,
117
- end: record.events.length,
118
- metadata: {
119
- batch_id: record.batchId,
120
- idempotency_key: record.idempotencyKey,
121
- sent_at: record.sentAt,
122
- },
123
- }
124
- : record.children[record.nextOrdinal];
125
- if (!child || child.end - child.start < 2)
126
- return null;
127
- const boundary = child.start + Math.ceil((child.end - child.start) / 2);
128
- const replacements = [
129
- { start: child.start, end: boundary, metadata: createMetadata() },
130
- { start: boundary, end: child.end, metadata: createMetadata() },
131
- ];
132
- return withChecksum({
133
- ...withoutChecksum(record),
134
- kind: "split_plan",
135
- children: record.kind === "pending"
136
- ? replacements
137
- : [
138
- ...record.children.slice(0, record.nextOrdinal),
139
- ...replacements,
140
- ...record.children.slice(record.nextOrdinal + 1),
141
- ],
142
- nextOrdinal: record.kind === "pending" ? 0 : record.nextOrdinal,
143
- });
144
- };
145
- export const largestFittingPrefix = (drafts, maxBytes, metadata, measure) => {
146
- let low = 1;
147
- let high = drafts.length;
148
- let largest = 0;
149
- while (low <= high) {
150
- const count = Math.floor((low + high) / 2);
151
- if (measure(drafts.slice(0, count).map((draft) => draft.event), metadata) <= maxBytes) {
152
- largest = count;
153
- low = count + 1;
154
- }
155
- else {
156
- high = count - 1;
157
- }
158
- }
159
- return largest;
160
- };
161
- export const ownEvent = (event, permit) => {
74
+ export const sentByteLength = (event) => new TextEncoder().encode(JSON.stringify(withSourceEvidence(event)))
75
+ .byteLength;
76
+ export const ownEvent = (event) => {
162
77
  try {
163
78
  const json = JSON.stringify(event);
164
79
  const parsed = JSON.parse(json);
165
80
  if (!isBrowserSourceSignalEvent(parsed))
166
81
  return null;
167
82
  return Object.freeze({
168
- byteLength: new TextEncoder().encode(json).byteLength,
169
- permit: Object.freeze({ ...permit }),
83
+ byteLength: sentByteLength(parsed),
170
84
  diagnosticCanonicalBytes: canonicalJsonStringify(parsed),
171
85
  value: () => JSON.parse(json),
172
86
  });
@@ -175,8 +89,6 @@ export const ownEvent = (event, permit) => {
175
89
  return null;
176
90
  }
177
91
  };
178
- export const isOwnedEvent = (value) => value !== null;
179
- export const recordBatchId = (batch) => batch.ownerBatchId;
180
92
  export const withChecksum = (record) => ({
181
93
  ...record,
182
94
  checksumSha256: sha256Hex(canonicalJsonStringify(record)),
@@ -188,7 +100,9 @@ export const withoutChecksum = (record) => {
188
100
  export const parseRecord = (raw, projectHash) => {
189
101
  try {
190
102
  const value = JSON.parse(raw);
191
- if (!isRecord(value) || value.storageVersion !== 2 || value.projectHash !== projectHash)
103
+ if (!isRecord(value) ||
104
+ value.storageVersion !== 2 ||
105
+ value.projectHash !== projectHash)
192
106
  return null;
193
107
  const checksumSha256 = value.checksumSha256;
194
108
  if (typeof checksumSha256 !== "string")
@@ -204,11 +118,14 @@ export const parseRecord = (raw, projectHash) => {
204
118
  };
205
119
  const isValidRecord = (value) => {
206
120
  const base = typeof value.createdAt === "string" &&
207
- (typeof value.recordVerifierSha256 === "string" || value.recordVerifierSha256 === null);
121
+ (typeof value.recordVerifierSha256 === "string" ||
122
+ value.recordVerifierSha256 === null);
208
123
  if (!base)
209
124
  return false;
210
125
  if (value.kind === "consent") {
211
- return ((value.state === "granted" || value.state === "denied" || value.state === "unknown") &&
126
+ return ((value.state === "granted" ||
127
+ value.state === "denied" ||
128
+ value.state === "unknown") &&
212
129
  typeof value.collectionGeneration === "string" &&
213
130
  typeof value.legacyImportBlocked === "boolean" &&
214
131
  typeof value.updatedAt === "string" &&
@@ -225,7 +142,8 @@ const isValidRecord = (value) => {
225
142
  isBrowserSourceSignalEvent(value.event));
226
143
  }
227
144
  if (value.kind === "legacy_tombstone") {
228
- return typeof value.eventKeyHash === "string" && typeof value.importedAt === "string";
145
+ return (typeof value.eventKeyHash === "string" &&
146
+ typeof value.importedAt === "string");
229
147
  }
230
148
  if (value.kind === "pending" || value.kind === "split_plan") {
231
149
  if (typeof value.batchId !== "string" ||
@@ -291,7 +209,8 @@ export const draftRecordKey = (prefix, eventKeyHash) => `${prefix}draft:${eventK
291
209
  export const pendingRecordKey = (prefix, batchId) => `${prefix}pending:${keyHash(batchId)}`;
292
210
  export const tombstoneRecordKey = (prefix, eventKeyHash) => `${prefix}legacy-tombstone:${eventKeyHash}`;
293
211
  export const browserEventsByteLength = (events) => events.reduce((total, event) => total + new TextEncoder().encode(JSON.stringify(event)).byteLength, 0);
294
- export const outboxEventCount = (drafts, pending) => drafts.length + pending.reduce((count, record) => count + record.events.length, 0);
212
+ export const outboxEventCount = (drafts, pending) => drafts.length +
213
+ pending.reduce((count, record) => count + record.events.length, 0);
295
214
  export const hasLegacyTombstone = (draft, tombstones) => tombstones.some((record) => record.eventKeyHash === draft.eventKeyHash &&
296
215
  record.recordVerifierSha256 === draft.recordVerifierSha256);
297
216
  export const createGeneration = () => typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
@@ -299,7 +218,9 @@ export const createGeneration = () => typeof crypto !== "undefined" && typeof cr
299
218
  : `${Date.now()}-${Math.random()}`;
300
219
  export const readStorage = () => {
301
220
  try {
302
- return typeof localStorage === "undefined" ? null : localStorage;
221
+ return typeof localStorage === "undefined"
222
+ ? null
223
+ : localStorage;
303
224
  }
304
225
  catch {
305
226
  return null;
@@ -1,10 +1,11 @@
1
1
  import type { InternalMetricsStore } from "../internal/metrics.js";
2
2
  import type { CluebaseConsentState } from "../provider/types.js";
3
+ import { type DraftRecord, type PendingRecordLike, type StorageLike } from "./browser-outbox-records.js";
3
4
  import type { BatchEnvelopeMetadata } from "./client.js";
4
5
  import { DiagnosticStore } from "./diagnostics.js";
6
+ import type { RestoredEvent } from "./sdk-core-delivery.js";
5
7
  import type { BrowserSourceEvent } from "./source-event.js";
6
- import { type CollectionPermit, type PendingBrowserBatch, type StorageLike } from "./browser-outbox-records.js";
7
- export type { CollectionPermit, OwnedBrowserEvent, PendingBrowserBatch } from "./browser-outbox-records.js";
8
+ export type { OwnedBrowserEvent } from "./browser-outbox-records.js";
8
9
  export type ConsentTransition = {
9
10
  readonly state: CluebaseConsentState;
10
11
  readonly generation: string;
@@ -21,17 +22,15 @@ export declare class BrowserOutbox {
21
22
  private readonly lockName;
22
23
  private readonly storage;
23
24
  private readonly durableOutboxSupported;
24
- private readonly maxBytes;
25
- private readonly maxEvents;
26
25
  private readonly metrics;
27
26
  private readonly diagnostics;
28
27
  private drafts;
29
28
  private pending;
29
+ private readonly pendingByBatchId;
30
30
  private tombstones;
31
31
  private nextOrdinal;
32
32
  private consent;
33
33
  private generation;
34
- private nextPermitOrdinal;
35
34
  private legacyImportBlocked;
36
35
  private consentAnchorEstablished;
37
36
  private initialDenialPending;
@@ -47,44 +46,44 @@ export declare class BrowserOutbox {
47
46
  constructor(options: {
48
47
  readonly projectKey: string;
49
48
  readonly initialConsent: CluebaseConsentState;
50
- readonly maxBytes: number;
51
- readonly maxEvents: number;
52
49
  readonly metrics?: InternalMetricsStore;
53
50
  readonly diagnostics?: DiagnosticStore;
54
51
  readonly storage?: StorageLike | null;
55
52
  readonly onConsentRevoked?: () => void;
56
53
  });
57
54
  currentConsent(): CluebaseConsentState;
58
- capturePermit(): CollectionPermit;
59
- permits(permit: CollectionPermit): boolean;
55
+ collectionGeneration(): string;
60
56
  setConsent(next: CluebaseConsentState): ConsentTransition;
61
57
  commitConsentTransition(transition: ConsentTransition): void;
62
58
  private persistCommittedConsent;
63
- addDraft(event: BrowserSourceEvent, permit?: CollectionPermit): boolean;
64
- estimatedDraftBytes(measure: (events: readonly BrowserSourceEvent[], metadata: BatchEnvelopeMetadata) => number, metadata: BatchEnvelopeMetadata): number;
65
- prepareFence(input: {
66
- readonly maxPayloadBytes: number;
59
+ holdDraft(event: BrowserSourceEvent): DraftRecord | null;
60
+ claimBatch(input: {
61
+ readonly batchId: number;
62
+ readonly eventKeys: readonly string[];
67
63
  readonly createMetadata: () => BatchEnvelopeMetadata;
68
- readonly measure: (events: readonly BrowserSourceEvent[], metadata: BatchEnvelopeMetadata) => number;
69
64
  }): {
70
- readonly targetOrdinal: number;
71
- readonly oversized: number;
65
+ readonly record: PendingRecordLike;
66
+ readonly minted: boolean;
67
+ } | null;
68
+ releaseBatch(batchId: number): void;
69
+ reopenBatch(batchId: number): void;
70
+ releaseEvents(eventKeys: readonly string[]): void;
71
+ restorable(): {
72
+ readonly batches: readonly {
73
+ readonly ordinal: number;
74
+ readonly storedBatchId: string;
75
+ readonly events: readonly RestoredEvent[];
76
+ }[];
77
+ readonly drafts: readonly DraftRecord[];
72
78
  };
73
- headThrough(targetOrdinal: number): PendingBrowserBatch | null;
74
- removeHead(expected: PendingBrowserBatch): boolean;
75
- retainHead(expected: PendingBrowserBatch): boolean;
76
- splitHead(expected: PendingBrowserBatch, createMetadata: () => BatchEnvelopeMetadata): boolean;
79
+ bindRestoredBatch(storedBatchId: string, batchId: number): void;
77
80
  clear(): boolean;
78
81
  close(): void;
79
82
  pendingEventCount(): number;
80
- pendingBytes(): number;
81
83
  withDeliveryLock<T>(operation: () => Promise<T>, options?: {
82
84
  readonly restore?: boolean;
83
85
  }): Promise<T>;
84
- private permitsBatch;
85
- private resolvePermit;
86
86
  private initializeConsentForDelivery;
87
- private enforceCapacity;
88
87
  private purgeAll;
89
88
  clearMemory(): void;
90
89
  private restore;
@@ -101,7 +100,6 @@ export declare class BrowserOutbox {
101
100
  private persistConsent;
102
101
  private writeRecord;
103
102
  private removeRecord;
104
- private removeDraft;
105
103
  private removeClaimedDraftRecords;
106
104
  private readRecord;
107
105
  private failClosedConsentState;