@clue-ai/browser-sdk 0.0.1

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 (102) hide show
  1. package/README.md +100 -0
  2. package/dist/authoring/overlay.d.ts +12 -0
  3. package/dist/authoring/overlay.js +468 -0
  4. package/dist/authoring/recording.d.ts +125 -0
  5. package/dist/authoring/recording.js +481 -0
  6. package/dist/authoring/service-logo.d.ts +1 -0
  7. package/dist/authoring/service-logo.generated.d.ts +1 -0
  8. package/dist/authoring/service-logo.generated.js +3 -0
  9. package/dist/authoring/service-logo.js +1 -0
  10. package/dist/authoring/session.d.ts +23 -0
  11. package/dist/authoring/session.js +127 -0
  12. package/dist/authoring/surface.d.ts +11 -0
  13. package/dist/authoring/surface.js +63 -0
  14. package/dist/authoring/toolbar-constants.d.ts +23 -0
  15. package/dist/authoring/toolbar-constants.js +42 -0
  16. package/dist/authoring/toolbar-drag.d.ts +29 -0
  17. package/dist/authoring/toolbar-drag.js +270 -0
  18. package/dist/authoring/toolbar-view.d.ts +21 -0
  19. package/dist/authoring/toolbar-view.js +2584 -0
  20. package/dist/capture/action.d.ts +2 -0
  21. package/dist/capture/action.js +62 -0
  22. package/dist/capture/dom.d.ts +23 -0
  23. package/dist/capture/dom.js +329 -0
  24. package/dist/capture/drag.d.ts +2 -0
  25. package/dist/capture/drag.js +75 -0
  26. package/dist/capture/error.d.ts +2 -0
  27. package/dist/capture/error.js +193 -0
  28. package/dist/capture/form.d.ts +2 -0
  29. package/dist/capture/form.js +137 -0
  30. package/dist/capture/frustration.d.ts +2 -0
  31. package/dist/capture/frustration.js +171 -0
  32. package/dist/capture/input.d.ts +2 -0
  33. package/dist/capture/input.js +109 -0
  34. package/dist/capture/location.d.ts +10 -0
  35. package/dist/capture/location.js +42 -0
  36. package/dist/capture/navigation.d.ts +2 -0
  37. package/dist/capture/navigation.js +100 -0
  38. package/dist/capture/network.d.ts +13 -0
  39. package/dist/capture/network.js +903 -0
  40. package/dist/capture/page.d.ts +2 -0
  41. package/dist/capture/page.js +78 -0
  42. package/dist/capture/performance.d.ts +2 -0
  43. package/dist/capture/performance.js +268 -0
  44. package/dist/context/account.d.ts +12 -0
  45. package/dist/context/account.js +129 -0
  46. package/dist/context/environment.d.ts +42 -0
  47. package/dist/context/environment.js +208 -0
  48. package/dist/context/identity.d.ts +14 -0
  49. package/dist/context/identity.js +123 -0
  50. package/dist/context/session.d.ts +28 -0
  51. package/dist/context/session.js +155 -0
  52. package/dist/context/tab.d.ts +22 -0
  53. package/dist/context/tab.js +142 -0
  54. package/dist/context/trace.d.ts +32 -0
  55. package/dist/context/trace.js +65 -0
  56. package/dist/core/config.d.ts +4 -0
  57. package/dist/core/config.js +199 -0
  58. package/dist/core/constants.d.ts +43 -0
  59. package/dist/core/constants.js +109 -0
  60. package/dist/core/contracts.d.ts +58 -0
  61. package/dist/core/contracts.js +53 -0
  62. package/dist/core/sdk.d.ts +2 -0
  63. package/dist/core/sdk.js +831 -0
  64. package/dist/core/types.d.ts +413 -0
  65. package/dist/core/types.js +1 -0
  66. package/dist/core/usage-governor.d.ts +7 -0
  67. package/dist/core/usage-governor.js +127 -0
  68. package/dist/index.d.ts +17 -0
  69. package/dist/index.js +36 -0
  70. package/dist/integrations/next-router.d.ts +16 -0
  71. package/dist/integrations/next-router.js +18 -0
  72. package/dist/integrations/react-router.d.ts +7 -0
  73. package/dist/integrations/react-router.js +37 -0
  74. package/dist/internal/metrics.d.ts +9 -0
  75. package/dist/internal/metrics.js +38 -0
  76. package/dist/normalize/builders.d.ts +15 -0
  77. package/dist/normalize/builders.js +786 -0
  78. package/dist/normalize/canonical.d.ts +13 -0
  79. package/dist/normalize/canonical.js +77 -0
  80. package/dist/normalize/event-id.d.ts +8 -0
  81. package/dist/normalize/event-id.js +39 -0
  82. package/dist/normalize/path-template.d.ts +1 -0
  83. package/dist/normalize/path-template.js +33 -0
  84. package/dist/privacy/local-minimization.d.ts +29 -0
  85. package/dist/privacy/local-minimization.js +88 -0
  86. package/dist/privacy/mask.d.ts +7 -0
  87. package/dist/privacy/mask.js +60 -0
  88. package/dist/privacy/parameter-snapshot.d.ts +14 -0
  89. package/dist/privacy/parameter-snapshot.js +206 -0
  90. package/dist/privacy/sanitize.d.ts +11 -0
  91. package/dist/privacy/sanitize.js +145 -0
  92. package/dist/privacy/schema-evidence.d.ts +20 -0
  93. package/dist/privacy/schema-evidence.js +238 -0
  94. package/dist/transport/batch.d.ts +37 -0
  95. package/dist/transport/batch.js +182 -0
  96. package/dist/transport/client.d.ts +61 -0
  97. package/dist/transport/client.js +267 -0
  98. package/dist/transport/queue.d.ts +22 -0
  99. package/dist/transport/queue.js +56 -0
  100. package/dist/transport/retry.d.ts +14 -0
  101. package/dist/transport/retry.js +46 -0
  102. package/package.json +38 -0
@@ -0,0 +1,413 @@
1
+ import type { InternalMetricsSnapshot } from "../internal/metrics";
2
+ import type { FrontendEventCategory, FrontendEventName, FrontendEventStatus, ParameterCaptureMode, SdkCollectionMode } from "./contracts";
3
+ export type { FrontendEventCategory, FrontendEventName, FrontendEventStatus, ParameterCaptureMode, SdkCollectionMode, } from "./contracts";
4
+ export type ClueConsentState = "granted" | "denied" | "unknown";
5
+ export type ClueEnvironment = string;
6
+ export type TransportProfile = "standard" | "authoring_realtime";
7
+ export type CanonicalJsonValue = string | number | boolean | null | CanonicalJsonValue[] | {
8
+ [key: string]: CanonicalJsonValue;
9
+ };
10
+ export type RequestLifecycleOutcome = "completed" | "failed" | "aborted" | "unknown";
11
+ export type DiagnosticCollectionConfig = {
12
+ enabledCollectors?: string[];
13
+ scope?: Record<string, string | number | boolean | null>;
14
+ ttlMs?: number;
15
+ expiresAt?: string;
16
+ budgetWindowMs?: number;
17
+ maxEventsPerWindow?: number;
18
+ sampleRate?: number;
19
+ reason?: string;
20
+ enabledBy?: string;
21
+ };
22
+ export type ClueInitOptions = {
23
+ endpoint: string;
24
+ projectKey: string;
25
+ environment: ClueEnvironment;
26
+ localSetupCheck?: {
27
+ endpoint?: string;
28
+ };
29
+ serviceKey?: string;
30
+ producerId?: string;
31
+ browserTokenProvider?: () => string | Promise<string>;
32
+ transportProfile?: TransportProfile;
33
+ autocapture?: boolean;
34
+ captureNetwork?: boolean;
35
+ captureErrors?: boolean;
36
+ captureFrustration?: boolean;
37
+ capturePerformance?: boolean;
38
+ network?: {
39
+ tracePropagationOrigins?: string[];
40
+ important4xxPathPrefixes?: string[];
41
+ interactionPropagationWindowMs?: number;
42
+ };
43
+ batch?: {
44
+ enabled?: boolean;
45
+ maxSize?: number;
46
+ flushIntervalMs?: number;
47
+ maxEventBytes?: number;
48
+ maxPayloadBytes?: number;
49
+ };
50
+ privacy?: {
51
+ deniedKeys?: string[];
52
+ allowedValuePaths?: string[];
53
+ };
54
+ sampling?: {
55
+ sessionSampleRate?: number;
56
+ };
57
+ collection?: {
58
+ mode?: SdkCollectionMode;
59
+ diagnostic?: DiagnosticCollectionConfig;
60
+ };
61
+ release?: {
62
+ frontendRelease?: string;
63
+ };
64
+ featureFlagsProvider?: () => string[];
65
+ experimentVariantProvider?: () => string | null;
66
+ };
67
+ export type ResolvedClueConfig = {
68
+ endpoint: string;
69
+ originalEndpoint: string;
70
+ localSetupCheckEndpoint: string | null;
71
+ projectKey: string;
72
+ environment: ClueEnvironment;
73
+ serviceKey: string;
74
+ producerId: string;
75
+ browserTokenProvider: (() => string | Promise<string>) | null;
76
+ transportProfile: TransportProfile;
77
+ autocapture: boolean;
78
+ captureNetwork: boolean;
79
+ captureErrors: boolean;
80
+ captureFrustration: boolean;
81
+ capturePerformance: boolean;
82
+ network: {
83
+ tracePropagationOrigins: string[];
84
+ important4xxPathPrefixes: string[];
85
+ interactionPropagationWindowMs: number;
86
+ };
87
+ batch: {
88
+ enabled: boolean;
89
+ maxSize: number;
90
+ flushIntervalMs: number;
91
+ maxEventBytes: number;
92
+ maxPayloadBytes: number;
93
+ };
94
+ privacy: {
95
+ deniedKeys: string[];
96
+ allowedValuePaths: string[];
97
+ };
98
+ sampling: {
99
+ sessionSampleRate: number;
100
+ };
101
+ collection: {
102
+ mode: SdkCollectionMode;
103
+ diagnostic: {
104
+ enabledCollectors: string[];
105
+ scope: Record<string, string | number | boolean | null>;
106
+ ttlMs: number;
107
+ expiresAt: string | null;
108
+ budgetWindowMs: number;
109
+ maxEventsPerWindow: number;
110
+ sampleRate: number;
111
+ reason: string | null;
112
+ enabledBy: string | null;
113
+ };
114
+ };
115
+ release: {
116
+ frontendRelease: string | null;
117
+ };
118
+ featureFlagsProvider?: () => string[];
119
+ experimentVariantProvider?: () => string | null;
120
+ };
121
+ export type ClueBrowserSdk = {
122
+ init(options: ClueInitOptions): void;
123
+ identify(userId: string, traits?: ClueSubjectTraits): void;
124
+ setAccount(accountId: string, traits?: ClueSubjectTraits): void;
125
+ logout(): void;
126
+ reset(): void;
127
+ track(eventName: string, properties?: Record<string, unknown>, metrics?: Record<string, number>): void;
128
+ setConsent(consent: ClueConsentState): void;
129
+ getInternalMetrics(): InternalMetricsSnapshot;
130
+ };
131
+ export type CostCapability = "event_collection" | "ai_generation" | "raw_archive" | "retention_growth";
132
+ export type LimitKind = "monthly_overage_amount_limit" | "internal_cost_limit" | "internal_limit" | "operator_global_monthly_cost_limit";
133
+ export type ThresholdStage = "info" | "warning" | "urgent" | "reached";
134
+ export type LimitReason = {
135
+ limitKind: LimitKind;
136
+ meterKey: string;
137
+ thresholdStage: ThresholdStage;
138
+ currentValue: number;
139
+ limitValue: number;
140
+ retryAfterAt: string | null;
141
+ };
142
+ export type IngestUsageGovernorFeedback = {
143
+ disabledCapabilities: CostCapability[];
144
+ limitReasons: LimitReason[];
145
+ retryAfterAt: string | null;
146
+ };
147
+ export type ParameterSnapshotLeafType = "string" | "number" | "boolean" | "null" | "object" | "array";
148
+ export type ParameterSnapshotLeaf = {
149
+ type: ParameterSnapshotLeafType;
150
+ value: unknown;
151
+ capture_mode: ParameterCaptureMode;
152
+ truncated_reason?: "max_event_bytes";
153
+ original_size_bytes?: number;
154
+ };
155
+ export type ParameterSnapshotValue = ParameterSnapshotLeaf | ParameterSnapshotValue[] | {
156
+ [key: string]: ParameterSnapshotValue;
157
+ };
158
+ export type CanonicalBaseEvent = {
159
+ event_id: string;
160
+ source_event_id?: string;
161
+ source_schema_version?: string;
162
+ source_event_type?: string;
163
+ source_event_kind?: string;
164
+ producer_id: string | null;
165
+ event_name: FrontendEventName;
166
+ custom_event_name?: string | null;
167
+ event_category: FrontendEventCategory;
168
+ event_source: "browser_sdk";
169
+ event_version: number;
170
+ occurred_at: string;
171
+ ingested_at: string;
172
+ status: FrontendEventStatus;
173
+ sdk_collection_mode: SdkCollectionMode;
174
+ /**
175
+ * Server-populated only. SDK runtime does not authoritatively stamp tenant identity.
176
+ */
177
+ tenant_id?: string | null;
178
+ anonymous_id: string | null;
179
+ user_id: string | null;
180
+ account_id: string | null;
181
+ organization_id?: string | null;
182
+ user_profile?: ClueSubjectProfile;
183
+ account_profile?: ClueSubjectProfile;
184
+ workspace_id: string | null;
185
+ session_id: string | null;
186
+ tab_id: string;
187
+ trace_id: string | null;
188
+ request_id: string | null;
189
+ request_span_id: string | null;
190
+ interaction_id: string | null;
191
+ correlation_id: string | null;
192
+ surface_type: "web";
193
+ environment: ClueEnvironment;
194
+ frontend_release: string | null;
195
+ backend_release: string | null;
196
+ feature_flags: string[];
197
+ experiment_variant: string | null;
198
+ device_type: string | null;
199
+ browser: string | null;
200
+ os: string | null;
201
+ locale: string | null;
202
+ country: string | null;
203
+ referrer: string | null;
204
+ browser_family?: string | null;
205
+ browser_major_version?: string | null;
206
+ browser_engine?: string | null;
207
+ os_family?: string | null;
208
+ os_major_version?: string | null;
209
+ timezone?: string | null;
210
+ pointer_type_candidate?: string | null;
211
+ touch_capable?: boolean | null;
212
+ cookies_enabled?: boolean | null;
213
+ local_storage_available?: boolean | null;
214
+ session_storage_available?: boolean | null;
215
+ service_worker_supported?: boolean | null;
216
+ webgl_supported?: boolean | null;
217
+ reduced_motion?: string | null;
218
+ color_scheme?: string | null;
219
+ account_status?: string | null;
220
+ plan_tier?: string | null;
221
+ billing_status?: string | null;
222
+ customer_lifecycle_stage?: string | null;
223
+ organization_size_bucket?: string | null;
224
+ user_role?: string | null;
225
+ permission_group?: string | null;
226
+ onboarding_state?: string | null;
227
+ trial_days_remaining_bucket?: string | null;
228
+ customer_health_bucket?: string | null;
229
+ collector_name?: string | null;
230
+ aggregation_kind?: string | null;
231
+ summary_window_ms?: number | null;
232
+ summary_count?: number | null;
233
+ dropped_count?: number | null;
234
+ sample_rate?: number | null;
235
+ budget_window_ms?: number | null;
236
+ rate_limit_key?: string | null;
237
+ performance_kind?: string | null;
238
+ rating?: string | null;
239
+ metric_name?: string | null;
240
+ metric_value_bucket?: string | null;
241
+ long_task_count?: number | null;
242
+ resource_failure_count?: number | null;
243
+ slow_resource_count?: number | null;
244
+ worst_duration_bucket?: string | null;
245
+ outcome_class?: string | null;
246
+ decision_type?: string | null;
247
+ decision_result?: string | null;
248
+ reason_code?: string | null;
249
+ attempt_status?: RequestLifecycleOutcome | null;
250
+ submit_result?: string | null;
251
+ result_source?: string | null;
252
+ request_schema_hash?: string | null;
253
+ response_schema_hash?: string | null;
254
+ request_shape_size?: number | null;
255
+ response_shape_size?: number | null;
256
+ masked_value_count?: number | null;
257
+ forbidden_value_count?: number | null;
258
+ truncated_value_count?: number | null;
259
+ allowed_value_keys?: string[];
260
+ view_id?: string | null;
261
+ screen_key?: string | null;
262
+ path?: string | null;
263
+ url_canonical?: string | null;
264
+ referrer_url_canonical?: string | null;
265
+ page_title_candidate?: string | null;
266
+ route_name_candidate?: string | null;
267
+ primary_heading_candidate?: string | null;
268
+ stable_key?: string | null;
269
+ stable_key_quality?: string | null;
270
+ form_key?: string | null;
271
+ from_view_id?: string | null;
272
+ to_view_id?: string | null;
273
+ from_screen_key?: string | null;
274
+ to_screen_key?: string | null;
275
+ from_path?: string | null;
276
+ to_path?: string | null;
277
+ method?: string | null;
278
+ path_template?: string | null;
279
+ status_code?: number | null;
280
+ duration_ms?: number | null;
281
+ target_service?: string | null;
282
+ time_on_page_ms?: number | null;
283
+ properties: Record<string, unknown>;
284
+ metrics: Record<string, number>;
285
+ request_parameters_snapshot_json?: ParameterSnapshotValue;
286
+ response_parameters_snapshot_json?: ParameterSnapshotValue;
287
+ request_metadata?: Record<string, unknown>;
288
+ response_metadata?: Record<string, unknown>;
289
+ target_object_hint?: CanonicalJsonValue;
290
+ };
291
+ export type ClueContext = {
292
+ producer_id: string | null;
293
+ anonymous_id: string | null;
294
+ user_id: string | null;
295
+ account_id: string | null;
296
+ organization_id?: string | null;
297
+ user_profile: ClueSubjectProfile | null;
298
+ account_profile: ClueSubjectProfile | null;
299
+ workspace_id: string | null;
300
+ session_id: string | null;
301
+ tab_id: string;
302
+ trace_id: string | null;
303
+ request_id: string | null;
304
+ request_span_id: string | null;
305
+ interaction_id: string | null;
306
+ correlation_id: string | null;
307
+ environment: ClueEnvironment;
308
+ sdk_collection_mode: SdkCollectionMode;
309
+ frontend_release: string | null;
310
+ feature_flags: string[];
311
+ experiment_variant: string | null;
312
+ plan: string | null;
313
+ role: string | null;
314
+ locale: string | null;
315
+ country: string | null;
316
+ device_type: string | null;
317
+ browser: string | null;
318
+ os: string | null;
319
+ referrer: string | null;
320
+ browser_family?: string | null;
321
+ browser_major_version?: string | null;
322
+ browser_engine?: string | null;
323
+ os_family?: string | null;
324
+ os_major_version?: string | null;
325
+ timezone?: string | null;
326
+ pointer_type_candidate?: string | null;
327
+ touch_capable?: boolean | null;
328
+ cookies_enabled?: boolean | null;
329
+ local_storage_available?: boolean | null;
330
+ session_storage_available?: boolean | null;
331
+ service_worker_supported?: boolean | null;
332
+ webgl_supported?: boolean | null;
333
+ reduced_motion?: string | null;
334
+ color_scheme?: string | null;
335
+ account_status?: string | null;
336
+ plan_tier?: string | null;
337
+ billing_status?: string | null;
338
+ customer_lifecycle_stage?: string | null;
339
+ organization_size_bucket?: string | null;
340
+ user_role?: string | null;
341
+ permission_group?: string | null;
342
+ onboarding_state?: string | null;
343
+ trial_days_remaining_bucket?: string | null;
344
+ customer_health_bucket?: string | null;
345
+ };
346
+ export type SessionContext = {
347
+ sessionId: string | null;
348
+ startedAtMs: number | null;
349
+ lastActivityAtMs: number | null;
350
+ };
351
+ export type IdentityContext = {
352
+ anonymousId: string | null;
353
+ userId: string | null;
354
+ traits: ClueSubjectTraits;
355
+ };
356
+ export type AccountContext = {
357
+ accountId: string | null;
358
+ organizationId: string | null;
359
+ workspaceId: string | null;
360
+ traits: ClueSubjectTraits;
361
+ };
362
+ export type ClueSubjectProfile = {
363
+ display_name: string | null;
364
+ avatar_url: string | null;
365
+ email: string | null;
366
+ role: string | null;
367
+ plan: string | null;
368
+ };
369
+ export type ClueSubjectTraits = Record<string, unknown> & {
370
+ plan?: string | null;
371
+ role?: string | null;
372
+ email?: string | null;
373
+ email_address?: string | null;
374
+ emailAddress?: string | null;
375
+ display_name?: string | null;
376
+ displayName?: string | null;
377
+ avatar_url?: string | null;
378
+ avatarUrl?: string | null;
379
+ workspace_id?: string | null;
380
+ workspaceId?: string | null;
381
+ organization_id?: string | null;
382
+ organizationId?: string | null;
383
+ };
384
+ export type ClueInternalLogger = {
385
+ debug: (message: string, details?: Record<string, unknown>) => void;
386
+ warn: (message: string, details?: Record<string, unknown>) => void;
387
+ error: (message: string, details?: Record<string, unknown>) => void;
388
+ };
389
+ export type RawEventName = FrontendEventName;
390
+ export type RawCapturedEvent = {
391
+ type: RawEventName;
392
+ occurredAtMs?: number;
393
+ payload: Record<string, unknown>;
394
+ };
395
+ export type CaptureEventHandler = (event: RawCapturedEvent) => void;
396
+ export type CaptureModuleContext = {
397
+ onEvent: CaptureEventHandler;
398
+ onInternalError: (source: string, error: unknown) => void;
399
+ getViewId: () => string | null;
400
+ getConsent: () => ClueConsentState;
401
+ getObservationContext?: () => {
402
+ anonymousId: string | null;
403
+ userId: string | null;
404
+ accountId: string | null;
405
+ organizationId?: string | null;
406
+ sessionId: string | null;
407
+ tabId: string | null;
408
+ };
409
+ shouldIgnoreElement?: (element: Element) => boolean;
410
+ };
411
+ export type CaptureStopHandle = {
412
+ stop: () => void;
413
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { CostCapability, IngestUsageGovernorFeedback } from "./types";
2
+ export declare const DEFAULT_INGEST_USAGE_GOVERNOR_FEEDBACK: IngestUsageGovernorFeedback;
3
+ export declare const normalizeIngestUsageGovernorFeedback: (feedback?: Partial<IngestUsageGovernorFeedback> | null) => IngestUsageGovernorFeedback;
4
+ export declare const mergeIngestUsageGovernorFeedback: (current: IngestUsageGovernorFeedback | null, incoming: IngestUsageGovernorFeedback | null) => IngestUsageGovernorFeedback | null;
5
+ export declare const parseGovernorFeedbackEnvelope: (value: unknown) => IngestUsageGovernorFeedback | null;
6
+ export declare const isCostCapabilityDisabled: (feedback: Pick<IngestUsageGovernorFeedback, "disabledCapabilities">, capability: CostCapability) => boolean;
7
+ export declare const hasUsageGovernorFeedbackChanged: (current: IngestUsageGovernorFeedback, next: IngestUsageGovernorFeedback) => boolean;
@@ -0,0 +1,127 @@
1
+ export const DEFAULT_INGEST_USAGE_GOVERNOR_FEEDBACK = {
2
+ disabledCapabilities: [],
3
+ limitReasons: [],
4
+ retryAfterAt: null,
5
+ };
6
+ const COST_CAPABILITIES = new Set([
7
+ "event_collection",
8
+ "ai_generation",
9
+ "raw_archive",
10
+ "retention_growth",
11
+ ]);
12
+ const LIMIT_KINDS = new Set([
13
+ "monthly_overage_amount_limit",
14
+ "internal_cost_limit",
15
+ "internal_limit",
16
+ "operator_global_monthly_cost_limit",
17
+ ]);
18
+ const THRESHOLD_STAGES = new Set([
19
+ "info",
20
+ "warning",
21
+ "urgent",
22
+ "reached",
23
+ ]);
24
+ const asRecord = (value) => {
25
+ if (value && typeof value === "object" && !Array.isArray(value)) {
26
+ return value;
27
+ }
28
+ return null;
29
+ };
30
+ const asCostCapability = (value) => {
31
+ return typeof value === "string" && COST_CAPABILITIES.has(value)
32
+ ? value
33
+ : null;
34
+ };
35
+ const asLimitKind = (value) => {
36
+ return typeof value === "string" && LIMIT_KINDS.has(value)
37
+ ? value
38
+ : null;
39
+ };
40
+ const asThresholdStage = (value) => {
41
+ return typeof value === "string" &&
42
+ THRESHOLD_STAGES.has(value)
43
+ ? value
44
+ : null;
45
+ };
46
+ const asNonNegativeNumber = (value) => {
47
+ return typeof value === "number" && Number.isFinite(value) && value >= 0
48
+ ? value
49
+ : null;
50
+ };
51
+ const asNullableString = (value) => {
52
+ return typeof value === "string" ? value : null;
53
+ };
54
+ const parseLimitReason = (value) => {
55
+ const record = asRecord(value);
56
+ if (!record) {
57
+ return null;
58
+ }
59
+ const limitKind = asLimitKind(record.limitKind);
60
+ const thresholdStage = asThresholdStage(record.thresholdStage);
61
+ const currentValue = asNonNegativeNumber(record.currentValue);
62
+ const limitValue = asNonNegativeNumber(record.limitValue);
63
+ const retryAfterAt = record.retryAfterAt === null ? null : asNullableString(record.retryAfterAt);
64
+ if (!limitKind ||
65
+ typeof record.meterKey !== "string" ||
66
+ !thresholdStage ||
67
+ currentValue === null ||
68
+ limitValue === null ||
69
+ retryAfterAt === undefined) {
70
+ return null;
71
+ }
72
+ return {
73
+ limitKind,
74
+ meterKey: record.meterKey,
75
+ thresholdStage,
76
+ currentValue,
77
+ limitValue,
78
+ retryAfterAt,
79
+ };
80
+ };
81
+ const uniqueCapabilities = (capabilities) => [...new Set(capabilities)];
82
+ export const normalizeIngestUsageGovernorFeedback = (feedback) => {
83
+ const disabledCapabilities = Array.isArray(feedback?.disabledCapabilities)
84
+ ? feedback.disabledCapabilities
85
+ .map(asCostCapability)
86
+ .filter((capability) => capability !== null)
87
+ : [];
88
+ const limitReasons = Array.isArray(feedback?.limitReasons)
89
+ ? feedback.limitReasons
90
+ .map(parseLimitReason)
91
+ .filter((reason) => reason !== null)
92
+ : [];
93
+ return {
94
+ disabledCapabilities: uniqueCapabilities(disabledCapabilities),
95
+ limitReasons,
96
+ retryAfterAt: typeof feedback?.retryAfterAt === "string" ? feedback.retryAfterAt : null,
97
+ };
98
+ };
99
+ export const mergeIngestUsageGovernorFeedback = (current, incoming) => {
100
+ if (!current) {
101
+ return incoming;
102
+ }
103
+ if (!incoming) {
104
+ return current;
105
+ }
106
+ return {
107
+ disabledCapabilities: uniqueCapabilities([
108
+ ...current.disabledCapabilities,
109
+ ...incoming.disabledCapabilities,
110
+ ]),
111
+ limitReasons: [...current.limitReasons, ...incoming.limitReasons],
112
+ retryAfterAt: current.retryAfterAt ?? incoming.retryAfterAt,
113
+ };
114
+ };
115
+ export const parseGovernorFeedbackEnvelope = (value) => {
116
+ const payload = asRecord(value);
117
+ const governor = asRecord(payload?.governor);
118
+ if (!governor) {
119
+ return null;
120
+ }
121
+ return normalizeIngestUsageGovernorFeedback(governor);
122
+ };
123
+ export const isCostCapabilityDisabled = (feedback, capability) => feedback.disabledCapabilities.includes(capability);
124
+ export const hasUsageGovernorFeedbackChanged = (current, next) => current.retryAfterAt !== next.retryAfterAt ||
125
+ current.disabledCapabilities.join("|") !==
126
+ next.disabledCapabilities.join("|") ||
127
+ JSON.stringify(current.limitReasons) !== JSON.stringify(next.limitReasons);
@@ -0,0 +1,17 @@
1
+ import { createClueBrowserSdk } from "./core/sdk";
2
+ import type { ClueBrowserSdk, ClueConsentState, ClueInitOptions, ClueSubjectTraits } from "./core/types";
3
+ import type { InternalMetricsSnapshot } from "./internal/metrics";
4
+ export declare const ClueInit: (options: ClueInitOptions) => void;
5
+ export declare const ClueIdentify: (userId: string, traits?: ClueSubjectTraits) => void;
6
+ export declare const ClueSetAccount: (accountId: string, traits?: ClueSubjectTraits) => void;
7
+ export declare const reset: () => void;
8
+ export declare const ClueLogout: () => void;
9
+ export declare const ClueTrack: (eventName: string, properties?: Record<string, unknown>, metrics?: Record<string, number>) => void;
10
+ export declare const setConsent: (consent: ClueConsentState) => void;
11
+ export declare const getInternalMetrics: () => InternalMetricsSnapshot;
12
+ export { attachNextRouterIntegration } from "./integrations/next-router";
13
+ export { attachReactRouterIntegration } from "./integrations/react-router";
14
+ export type { ClueBrowserSdk, ClueConsentState, ClueInitOptions, ClueSubjectTraits, InternalMetricsSnapshot, };
15
+ export { createClueBrowserSdk };
16
+ declare const _default: ClueBrowserSdk;
17
+ export default _default;
package/dist/index.js ADDED
@@ -0,0 +1,36 @@
1
+ import { createClueBrowserSdk } from "./core/sdk";
2
+ const GLOBAL_SDK_KEY = "__clue_browser_sdk_singleton__";
3
+ const globalWithClueSdk = globalThis;
4
+ const existingSdk = globalWithClueSdk[GLOBAL_SDK_KEY];
5
+ const sdk = existingSdk ?? createClueBrowserSdk();
6
+ if (!existingSdk) {
7
+ globalWithClueSdk[GLOBAL_SDK_KEY] = sdk;
8
+ }
9
+ export const ClueInit = (options) => {
10
+ sdk.init(options);
11
+ };
12
+ export const ClueIdentify = (userId, traits) => {
13
+ sdk.identify(userId, traits);
14
+ };
15
+ export const ClueSetAccount = (accountId, traits) => {
16
+ sdk.setAccount(accountId, traits);
17
+ };
18
+ export const reset = () => {
19
+ sdk.reset();
20
+ };
21
+ export const ClueLogout = () => {
22
+ sdk.logout();
23
+ };
24
+ export const ClueTrack = (eventName, properties, metrics) => {
25
+ sdk.track(eventName, properties, metrics);
26
+ };
27
+ export const setConsent = (consent) => {
28
+ sdk.setConsent(consent);
29
+ };
30
+ export const getInternalMetrics = () => {
31
+ return sdk.getInternalMetrics();
32
+ };
33
+ export { attachNextRouterIntegration } from "./integrations/next-router";
34
+ export { attachReactRouterIntegration } from "./integrations/react-router";
35
+ export { createClueBrowserSdk };
36
+ export default sdk;
@@ -0,0 +1,16 @@
1
+ export type NextRouterEventsLike = {
2
+ on: (event: string, handler: (...args: unknown[]) => void) => void;
3
+ off: (event: string, handler: (...args: unknown[]) => void) => void;
4
+ };
5
+ export type NextRouterLike = {
6
+ asPath?: string;
7
+ events: NextRouterEventsLike;
8
+ };
9
+ export type NextRouterIntegrationOptions = {
10
+ router: NextRouterLike;
11
+ onRouteChange: (fromPath: string, toPath: string) => void;
12
+ };
13
+ export type NextRouterIntegrationHandle = {
14
+ detach: () => void;
15
+ };
16
+ export declare const attachNextRouterIntegration: (options: NextRouterIntegrationOptions) => NextRouterIntegrationHandle;
@@ -0,0 +1,18 @@
1
+ export const attachNextRouterIntegration = (options) => {
2
+ const { router, onRouteChange } = options;
3
+ let lastPath = router.asPath ?? "";
4
+ const handler = (nextUrl) => {
5
+ const nextPath = typeof nextUrl === "string" ? nextUrl : (router.asPath ?? "");
6
+ if (!nextPath || nextPath === lastPath) {
7
+ return;
8
+ }
9
+ onRouteChange(lastPath, nextPath);
10
+ lastPath = nextPath;
11
+ };
12
+ router.events.on("routeChangeComplete", handler);
13
+ return {
14
+ detach: () => {
15
+ router.events.off("routeChangeComplete", handler);
16
+ },
17
+ };
18
+ };
@@ -0,0 +1,7 @@
1
+ export type ReactRouterIntegrationOptions = {
2
+ onRouteChange: (fromPath: string, toPath: string) => void;
3
+ };
4
+ export type ReactRouterIntegrationHandle = {
5
+ detach: () => void;
6
+ };
7
+ export declare const attachReactRouterIntegration: (options: ReactRouterIntegrationOptions) => ReactRouterIntegrationHandle;
@@ -0,0 +1,37 @@
1
+ export const attachReactRouterIntegration = (options) => {
2
+ if (typeof window === "undefined") {
3
+ return {
4
+ detach: () => undefined,
5
+ };
6
+ }
7
+ let lastPath = window.location.pathname;
8
+ const emit = () => {
9
+ const nextPath = window.location.pathname;
10
+ if (nextPath === lastPath) {
11
+ return;
12
+ }
13
+ options.onRouteChange(lastPath, nextPath);
14
+ lastPath = nextPath;
15
+ };
16
+ const originalPushState = history.pushState;
17
+ const originalReplaceState = history.replaceState;
18
+ history.pushState = function (...args) {
19
+ originalPushState.apply(this, args);
20
+ emit();
21
+ };
22
+ history.replaceState = function (...args) {
23
+ originalReplaceState.apply(this, args);
24
+ emit();
25
+ };
26
+ const popstateHandler = () => {
27
+ emit();
28
+ };
29
+ window.addEventListener("popstate", popstateHandler, true);
30
+ return {
31
+ detach: () => {
32
+ history.pushState = originalPushState;
33
+ history.replaceState = originalReplaceState;
34
+ window.removeEventListener("popstate", popstateHandler, true);
35
+ },
36
+ };
37
+ };