@squasher-ai/browser 0.1.0

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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +12 -0
  3. package/dist/__tests__/client.test.d.ts +2 -0
  4. package/dist/__tests__/client.test.d.ts.map +1 -0
  5. package/dist/__tests__/client.test.js +103 -0
  6. package/dist/__tests__/errors.test.d.ts +2 -0
  7. package/dist/__tests__/errors.test.d.ts.map +1 -0
  8. package/dist/__tests__/errors.test.js +80 -0
  9. package/dist/__tests__/replay-privacy.test.d.ts +2 -0
  10. package/dist/__tests__/replay-privacy.test.d.ts.map +1 -0
  11. package/dist/__tests__/replay-privacy.test.js +124 -0
  12. package/dist/__tests__/replay-startup.test.d.ts +2 -0
  13. package/dist/__tests__/replay-startup.test.d.ts.map +1 -0
  14. package/dist/__tests__/replay-startup.test.js +99 -0
  15. package/dist/__tests__/replay.test.d.ts +2 -0
  16. package/dist/__tests__/replay.test.d.ts.map +1 -0
  17. package/dist/__tests__/replay.test.js +217 -0
  18. package/dist/__tests__/session.test.d.ts +2 -0
  19. package/dist/__tests__/session.test.d.ts.map +1 -0
  20. package/dist/__tests__/session.test.js +46 -0
  21. package/dist/__tests__/transport.test.d.ts +2 -0
  22. package/dist/__tests__/transport.test.d.ts.map +1 -0
  23. package/dist/__tests__/transport.test.js +101 -0
  24. package/dist/__tests__/types.test.d.ts +2 -0
  25. package/dist/__tests__/types.test.d.ts.map +1 -0
  26. package/dist/__tests__/types.test.js +139 -0
  27. package/dist/autocapture.d.ts +21 -0
  28. package/dist/autocapture.d.ts.map +1 -0
  29. package/dist/autocapture.js +191 -0
  30. package/dist/client.d.ts +46 -0
  31. package/dist/client.d.ts.map +1 -0
  32. package/dist/client.js +215 -0
  33. package/dist/errors.d.ts +29 -0
  34. package/dist/errors.d.ts.map +1 -0
  35. package/dist/errors.js +111 -0
  36. package/dist/index.d.ts +62 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +105 -0
  39. package/dist/react.d.ts +32 -0
  40. package/dist/react.d.ts.map +1 -0
  41. package/dist/react.js +42 -0
  42. package/dist/replay-privacy.d.ts +16 -0
  43. package/dist/replay-privacy.d.ts.map +1 -0
  44. package/dist/replay-privacy.js +64 -0
  45. package/dist/replay.d.ts +30 -0
  46. package/dist/replay.d.ts.map +1 -0
  47. package/dist/replay.js +158 -0
  48. package/dist/session.d.ts +21 -0
  49. package/dist/session.d.ts.map +1 -0
  50. package/dist/session.js +52 -0
  51. package/dist/telemetry.d.ts +20 -0
  52. package/dist/telemetry.d.ts.map +1 -0
  53. package/dist/telemetry.js +145 -0
  54. package/dist/transport.d.ts +47 -0
  55. package/dist/transport.d.ts.map +1 -0
  56. package/dist/transport.js +145 -0
  57. package/dist/types.d.ts +234 -0
  58. package/dist/types.d.ts.map +1 -0
  59. package/dist/types.js +8 -0
  60. package/dist/vitals.d.ts +30 -0
  61. package/dist/vitals.d.ts.map +1 -0
  62. package/dist/vitals.js +92 -0
  63. package/package.json +44 -0
@@ -0,0 +1,145 @@
1
+ import { getSessionId } from "./session";
2
+ export function mergeIdentifiedUser(currentUser, contextUser, distinctId) {
3
+ return {
4
+ ...currentUser,
5
+ ...contextUser,
6
+ id: distinctId,
7
+ };
8
+ }
9
+ export function buildTrackEvent(eventName, properties, context) {
10
+ return {
11
+ ...context,
12
+ message: context.message ?? eventName,
13
+ level: context.level ?? "info",
14
+ kind: context.kind ?? "analytics",
15
+ event_name: context.event_name ?? eventName,
16
+ analytics: {
17
+ ...context.analytics,
18
+ event: context.analytics?.event ?? eventName,
19
+ properties: properties ?? context.analytics?.properties,
20
+ },
21
+ };
22
+ }
23
+ export function buildIdentifyEvent(distinctId, traits, context) {
24
+ return {
25
+ ...context,
26
+ message: context.message ?? `identify:${distinctId}`,
27
+ level: context.level ?? "info",
28
+ kind: "identify",
29
+ event_name: context.event_name ?? "identify",
30
+ distinct_id: context.distinct_id ?? distinctId,
31
+ analytics: {
32
+ ...context.analytics,
33
+ event: context.analytics?.event ?? "identify",
34
+ properties: traits ?? context.analytics?.properties,
35
+ },
36
+ user: {
37
+ ...context.user,
38
+ id: distinctId,
39
+ },
40
+ };
41
+ }
42
+ export function buildPageEvent(name, properties, context) {
43
+ return {
44
+ ...context,
45
+ message: context.message ?? `page:${name}`,
46
+ level: context.level ?? "info",
47
+ kind: "page",
48
+ event_name: context.event_name ?? name,
49
+ analytics: {
50
+ ...context.analytics,
51
+ event: context.analytics?.event ?? name,
52
+ properties: properties ?? context.analytics?.properties,
53
+ },
54
+ page: {
55
+ ...context.page,
56
+ name: context.page?.name ?? name,
57
+ path: context.page?.path ?? (typeof location !== "undefined" ? location.pathname : undefined),
58
+ title: context.page?.title ?? (typeof document !== "undefined" ? document.title : undefined),
59
+ referrer: context.page?.referrer ?? (typeof document !== "undefined" ? document.referrer : undefined),
60
+ search: context.page?.search ?? (typeof location !== "undefined" ? location.search : undefined),
61
+ },
62
+ };
63
+ }
64
+ export function buildScreenEvent(name, properties, context) {
65
+ return {
66
+ ...context,
67
+ message: context.message ?? `screen:${name}`,
68
+ level: context.level ?? "info",
69
+ kind: "screen",
70
+ event_name: context.event_name ?? name,
71
+ analytics: {
72
+ ...context.analytics,
73
+ event: context.analytics?.event ?? name,
74
+ properties: properties ?? context.analytics?.properties,
75
+ },
76
+ page: {
77
+ ...context.page,
78
+ name: context.page?.name ?? name,
79
+ screen_class: context.page?.screen_class ?? name,
80
+ },
81
+ };
82
+ }
83
+ export function buildSpanEvent(name, context) {
84
+ return {
85
+ ...context,
86
+ message: context.message ?? `span:${name}`,
87
+ level: context.level ?? "info",
88
+ kind: context.kind ?? "agent_span",
89
+ event_name: context.event_name ?? name,
90
+ trace: {
91
+ ...context.trace,
92
+ span_name: context.trace?.span_name ?? name,
93
+ },
94
+ };
95
+ }
96
+ export function buildToolCallEvent(name, context) {
97
+ return {
98
+ ...context,
99
+ message: context.message ?? `tool:${name}`,
100
+ level: context.level ?? "info",
101
+ kind: "tool_call",
102
+ event_name: context.event_name ?? name,
103
+ tool_call: {
104
+ ...context.tool_call,
105
+ name: context.tool_call?.name ?? name,
106
+ },
107
+ };
108
+ }
109
+ export function buildGenerationEvent(message, context) {
110
+ return {
111
+ ...context,
112
+ message,
113
+ level: context.level ?? "info",
114
+ kind: "llm_generation",
115
+ event_name: context.event_name ?? context.llm?.model ?? "llm_generation",
116
+ };
117
+ }
118
+ export function prepareBrowserEvent(event, input) {
119
+ const filtered = input.beforeSend ? input.beforeSend(event) : event;
120
+ if (!filtered) {
121
+ return null;
122
+ }
123
+ const prepared = {
124
+ ...filtered,
125
+ session_id: getSessionId(),
126
+ environment: filtered.environment ?? input.environment,
127
+ release: filtered.release ?? input.release,
128
+ kind: filtered.kind ?? (filtered.level === "error" || filtered.level === "fatal" ? "error" : "log"),
129
+ ...(input.user ? { user: { ...input.user } } : {}),
130
+ ...(Object.keys(input.tags).length > 0 ? { tags: { ...input.tags, ...filtered.tags } } : {}),
131
+ ...(input.breadcrumbs.length > 0 ? { breadcrumbs: [...input.breadcrumbs] } : {}),
132
+ };
133
+ if (!prepared.visitor &&
134
+ prepared.kind &&
135
+ ["analytics", "identify", "page", "screen", "visitor"].includes(prepared.kind)) {
136
+ prepared.visitor = { anonymous_id: prepared.session_id };
137
+ }
138
+ if (typeof location !== "undefined") {
139
+ prepared.request = {
140
+ ...prepared.request,
141
+ url: prepared.request?.url ?? location.href,
142
+ };
143
+ }
144
+ return prepared;
145
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Transport layer for the browser SDK.
3
+ *
4
+ * Two send paths:
5
+ * 1. Errors → POST /v1/ingest/{project_id} (immediate, same wire format as sdk-node)
6
+ * 2. Vitals → POST /v1/vitals/{project_id} (batched array)
7
+ *
8
+ * Primary transport: navigator.sendBeacon() — fire-and-forget, survives page unload.
9
+ * Fallback: fetch() with keepalive: true.
10
+ *
11
+ * Vitals are buffered and flushed on:
12
+ * - Buffer reaching vitalsBufferSize (default: 10)
13
+ * - Timer tick (default: 10s)
14
+ * - Page visibility change to "hidden" (user leaving)
15
+ */
16
+ import type { BrowserErrorEvent, VitalEvent } from "./types";
17
+ export interface TransportConfig {
18
+ endpoint: string;
19
+ projectId: string;
20
+ apiKey: string;
21
+ debug: boolean;
22
+ vitalsBufferSize: number;
23
+ vitalsFlushIntervalMs: number;
24
+ }
25
+ export declare class Transport {
26
+ private config;
27
+ private vitalsBuffer;
28
+ private flushTimer;
29
+ private disposed;
30
+ constructor(config: TransportConfig);
31
+ /** Send a single telemetry event immediately. Fire-and-forget. */
32
+ sendEvent(event: BrowserErrorEvent): void;
33
+ /** Backward-compatible alias for error events. */
34
+ sendError(event: BrowserErrorEvent): void;
35
+ /** Buffer a vital event. Auto-flushes when buffer is full. */
36
+ enqueueVital(event: VitalEvent): void;
37
+ /** Flush all buffered vitals immediately. */
38
+ flushVitals(): void;
39
+ /** Stop all timers and flush remaining data. */
40
+ dispose(): void;
41
+ private buildIngestUrl;
42
+ /** Try sending via navigator.sendBeacon. Returns true if successful. */
43
+ private trySendBeacon;
44
+ /** Send via fetch with keepalive and the API key header. */
45
+ private sendFetch;
46
+ }
47
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAiB,MAAM,SAAS,CAAC;AAE5E,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,QAAQ,CAAS;gBAEb,MAAM,EAAE,eAAe;IA2BnC,kEAAkE;IAClE,SAAS,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAoBzC,kDAAkD;IAClD,SAAS,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAMzC,8DAA8D;IAC9D,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAUrC,6CAA6C;IAC7C,WAAW,IAAI,IAAI;IAoBnB,gDAAgD;IAChD,OAAO,IAAI,IAAI;IAWf,OAAO,CAAC,cAAc;IAQtB,wEAAwE;IACxE,OAAO,CAAC,aAAa;IAarB,4DAA4D;YAC9C,SAAS;CAoBxB"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Transport layer for the browser SDK.
3
+ *
4
+ * Two send paths:
5
+ * 1. Errors → POST /v1/ingest/{project_id} (immediate, same wire format as sdk-node)
6
+ * 2. Vitals → POST /v1/vitals/{project_id} (batched array)
7
+ *
8
+ * Primary transport: navigator.sendBeacon() — fire-and-forget, survives page unload.
9
+ * Fallback: fetch() with keepalive: true.
10
+ *
11
+ * Vitals are buffered and flushed on:
12
+ * - Buffer reaching vitalsBufferSize (default: 10)
13
+ * - Timer tick (default: 10s)
14
+ * - Page visibility change to "hidden" (user leaving)
15
+ */
16
+ export class Transport {
17
+ config;
18
+ vitalsBuffer = [];
19
+ flushTimer = null;
20
+ disposed = false;
21
+ constructor(config) {
22
+ this.config = config;
23
+ // Start the vitals flush timer
24
+ this.flushTimer = setInterval(() => {
25
+ this.flushVitals();
26
+ }, config.vitalsFlushIntervalMs);
27
+ // Flush vitals when the page is being hidden (user leaving/switching tabs)
28
+ if (typeof document !== "undefined") {
29
+ document.addEventListener("visibilitychange", () => {
30
+ if (document.visibilityState === "hidden") {
31
+ this.flushVitals();
32
+ }
33
+ });
34
+ }
35
+ // Also flush on pagehide (more reliable than unload)
36
+ if (typeof window !== "undefined") {
37
+ window.addEventListener("pagehide", () => {
38
+ this.flushVitals();
39
+ });
40
+ }
41
+ }
42
+ // ─── Error sending (immediate) ──────────────────────────────────────
43
+ /** Send a single telemetry event immediately. Fire-and-forget. */
44
+ sendEvent(event) {
45
+ if (this.disposed)
46
+ return;
47
+ const url = this.buildIngestUrl();
48
+ const body = JSON.stringify(event);
49
+ // Try sendBeacon first (survives page unload)
50
+ if (this.trySendBeacon(this.buildIngestUrl({ beacon: true }), body)) {
51
+ if (this.config.debug) {
52
+ console.log("[squasher] Error sent via sendBeacon");
53
+ }
54
+ return;
55
+ }
56
+ // Fallback to fetch with keepalive
57
+ this.sendFetch(url, body).catch(() => {
58
+ // Fire-and-forget — errors are best-effort in the browser
59
+ });
60
+ }
61
+ /** Backward-compatible alias for error events. */
62
+ sendError(event) {
63
+ this.sendEvent(event);
64
+ }
65
+ // ─── Vitals buffering ───────────────────────────────────────────────
66
+ /** Buffer a vital event. Auto-flushes when buffer is full. */
67
+ enqueueVital(event) {
68
+ if (this.disposed)
69
+ return;
70
+ this.vitalsBuffer.push(event);
71
+ if (this.vitalsBuffer.length >= this.config.vitalsBufferSize) {
72
+ this.flushVitals();
73
+ }
74
+ }
75
+ /** Flush all buffered vitals immediately. */
76
+ flushVitals() {
77
+ if (this.vitalsBuffer.length === 0)
78
+ return;
79
+ const events = this.vitalsBuffer.splice(0);
80
+ const url = `${this.config.endpoint}/v1/vitals/${this.config.projectId}`;
81
+ const payload = { events };
82
+ const body = JSON.stringify(payload);
83
+ if (this.trySendBeacon(url, body)) {
84
+ if (this.config.debug) {
85
+ console.log(`[squasher] Flushed ${events.length} vitals via sendBeacon`);
86
+ }
87
+ return;
88
+ }
89
+ this.sendFetch(url, body).catch(() => {
90
+ // Best-effort
91
+ });
92
+ }
93
+ /** Stop all timers and flush remaining data. */
94
+ dispose() {
95
+ this.disposed = true;
96
+ if (this.flushTimer) {
97
+ clearInterval(this.flushTimer);
98
+ this.flushTimer = null;
99
+ }
100
+ this.flushVitals();
101
+ }
102
+ // ─── Private helpers ────────────────────────────────────────────────
103
+ buildIngestUrl(options) {
104
+ const baseUrl = `${this.config.endpoint}/v1/ingest/${this.config.projectId}`;
105
+ if (!options?.beacon) {
106
+ return baseUrl;
107
+ }
108
+ return `${baseUrl}?key=${encodeURIComponent(this.config.apiKey)}`;
109
+ }
110
+ /** Try sending via navigator.sendBeacon. Returns true if successful. */
111
+ trySendBeacon(url, body) {
112
+ if (typeof navigator === "undefined" || !navigator.sendBeacon) {
113
+ return false;
114
+ }
115
+ try {
116
+ const blob = new Blob([body], { type: "application/json" });
117
+ return navigator.sendBeacon(url, blob);
118
+ }
119
+ catch {
120
+ return false;
121
+ }
122
+ }
123
+ /** Send via fetch with keepalive and the API key header. */
124
+ async sendFetch(url, body) {
125
+ try {
126
+ await fetch(url, {
127
+ method: "POST",
128
+ headers: {
129
+ "Content-Type": "application/json",
130
+ "x-squasher-key": this.config.apiKey,
131
+ },
132
+ body,
133
+ keepalive: true,
134
+ });
135
+ if (this.config.debug) {
136
+ console.log("[squasher] Event sent via fetch");
137
+ }
138
+ }
139
+ catch (err) {
140
+ if (this.config.debug) {
141
+ console.warn("[squasher] Failed to send event:", err);
142
+ }
143
+ }
144
+ }
145
+ }
@@ -0,0 +1,234 @@
1
+ /**
2
+ * @squasher-ai/browser — Type definitions for the browser SDK.
3
+ *
4
+ * Defines its own types to avoid runtime dependency on @squasher-ai/node or
5
+ * @squasher-ai/api-spec. Shapes match the canonical wire protocol in
6
+ * packages/sdk-spec/protocol.schema.json.
7
+ */
8
+ export interface BrowserConfig {
9
+ /** API key (sq_pk_...). Required. */
10
+ apiKey: string;
11
+ /** Project ID (UUID). Required. */
12
+ projectId: string;
13
+ /** Ingestion endpoint. Default: "https://ingest.squasher.ai" */
14
+ endpoint?: string;
15
+ /** Environment tag (production, staging, development). */
16
+ environment?: string;
17
+ /** Release/version tag. */
18
+ release?: string;
19
+ /** Enable debug logging to console. Default: false. */
20
+ debug?: boolean;
21
+ /** Error sampling rate 0-1. Default: 1 (capture everything). */
22
+ sampleRate?: number;
23
+ /** Web Vitals sampling rate 0-1. Default: 1. */
24
+ vitalsSampleRate?: number;
25
+ /** Enable Core Web Vitals collection. Default: true. */
26
+ enableVitals?: boolean;
27
+ /** Enable global error capture (onerror, unhandledrejection). Default: true. */
28
+ enableErrorCapture?: boolean;
29
+ /** Enable auto-capture breadcrumbs (navigation, clicks, fetch). Default: true. */
30
+ enableAutoBreadcrumbs?: boolean;
31
+ /**
32
+ * Hook invoked before every error event is sent.
33
+ * Return the event (possibly modified) to send, or null to drop.
34
+ */
35
+ beforeSend?: (event: BrowserErrorEvent) => BrowserErrorEvent | null;
36
+ /** Max breadcrumbs to retain. Default: 30. */
37
+ maxBreadcrumbs?: number;
38
+ /** Max vitals to buffer before flush. Default: 10. */
39
+ vitalsBufferSize?: number;
40
+ /** Vitals flush interval in ms. Default: 10000. */
41
+ vitalsFlushIntervalMs?: number;
42
+ /** Optional session replay recording powered by rrweb. */
43
+ replay?: ReplayConfig;
44
+ }
45
+ export interface ReplayConfig {
46
+ /** Enable rrweb session replay recording for this browser session. */
47
+ enabled: boolean;
48
+ /** Session-level replay sampling rate from 0-1. Default: 1. */
49
+ sampleRate?: number;
50
+ /** Optional privacy controls applied to rrweb session replay recording. */
51
+ privacy?: ReplayPrivacyConfig;
52
+ }
53
+ export interface ReplayPrivacyConfig {
54
+ /** Replace recorded DOM text and typed input values with masked characters. */
55
+ maskAllText?: boolean;
56
+ /** CSS selector for elements that should render as blocked placeholders. */
57
+ blockSelector?: string;
58
+ }
59
+ export type VitalMetricName = "LCP" | "CLS" | "INP" | "FCP" | "TTFB";
60
+ export type VitalRating = "good" | "needs-improvement" | "poor";
61
+ export type DeviceType = "mobile" | "tablet" | "desktop";
62
+ /** Single vital measurement sent to POST /v1/vitals/{project_id}. */
63
+ export interface VitalEvent {
64
+ type: "vital";
65
+ /** Metric name (LCP, CLS, INP, FCP, TTFB). */
66
+ name: VitalMetricName;
67
+ /** Metric value (ms for timing metrics, unitless for CLS). */
68
+ value: number;
69
+ /** Rating based on Google thresholds. */
70
+ rating: VitalRating;
71
+ /** Change since last report (useful for CLS which accumulates). */
72
+ delta: number;
73
+ /** How the user navigated here (navigate, reload, back_forward, prerender). */
74
+ navigationType: string;
75
+ /** Full URL where this measurement was taken. */
76
+ url: string;
77
+ /** Optional route pattern (e.g. /products/:id). User-configurable. */
78
+ route?: string;
79
+ /** Session ID linking vitals to errors from the same browser session. */
80
+ session_id: string;
81
+ /** Epoch ms timestamp. */
82
+ timestamp: number;
83
+ /** Environment tag from config. */
84
+ environment?: string;
85
+ /** Release tag from config. */
86
+ release?: string;
87
+ /** Network connection type (4g, 3g, slow-2g). Via navigator.connection. */
88
+ connection?: string;
89
+ /** Device classification based on screen width. */
90
+ device_type?: DeviceType;
91
+ /** Document referrer. */
92
+ referrer?: string;
93
+ }
94
+ /** Batched payload sent to the vitals endpoint. */
95
+ export interface VitalsPayload {
96
+ events: VitalEvent[];
97
+ }
98
+ export type Level = "fatal" | "error" | "warning" | "info" | "debug";
99
+ export type TelemetryKind = "error" | "log" | "analytics" | "identify" | "page" | "screen" | "visitor" | "agent_session" | "agent_span" | "tool_call" | "llm_generation";
100
+ export type JsonPrimitive = string | number | boolean | null;
101
+ export type JsonValue = JsonPrimitive | JsonObject | JsonValue[];
102
+ export interface JsonObject {
103
+ [key: string]: JsonValue;
104
+ }
105
+ export interface VisitorContext {
106
+ visitor_id?: string;
107
+ anonymous_id?: string;
108
+ account_id?: string;
109
+ }
110
+ export interface AnalyticsContext {
111
+ event?: string;
112
+ category?: string;
113
+ funnel?: string;
114
+ step?: string;
115
+ properties?: JsonObject;
116
+ }
117
+ export interface PageContext {
118
+ name?: string;
119
+ path?: string;
120
+ title?: string;
121
+ referrer?: string;
122
+ search?: string;
123
+ screen_class?: string;
124
+ }
125
+ export interface SessionContext {
126
+ session_type?: string;
127
+ agent_id?: string;
128
+ run_id?: string;
129
+ workflow_id?: string;
130
+ step_id?: string;
131
+ status?: string;
132
+ duration_ms?: number;
133
+ }
134
+ export interface TraceContext {
135
+ trace_id?: string;
136
+ span_id?: string;
137
+ parent_span_id?: string;
138
+ span_name?: string;
139
+ span_kind?: string;
140
+ status?: string;
141
+ duration_ms?: number;
142
+ }
143
+ export interface ToolCallContext {
144
+ name?: string;
145
+ input?: JsonObject;
146
+ output?: JsonObject;
147
+ status?: string;
148
+ duration_ms?: number;
149
+ }
150
+ export interface LlmContext {
151
+ provider?: string;
152
+ model?: string;
153
+ prompt_tokens?: number;
154
+ completion_tokens?: number;
155
+ total_tokens?: number;
156
+ cost_usd?: number;
157
+ latency_ms?: number;
158
+ status?: string;
159
+ input?: JsonObject;
160
+ output?: JsonObject;
161
+ }
162
+ export interface TelemetryMeasurement {
163
+ name: string;
164
+ value: number;
165
+ unit?: string;
166
+ }
167
+ export interface BrowserErrorEvent {
168
+ message: string;
169
+ type?: string;
170
+ stack?: string;
171
+ frames?: StackFrame[];
172
+ level?: Level;
173
+ tags?: Record<string, string>;
174
+ user?: UserContext;
175
+ request?: {
176
+ url: string;
177
+ method?: string;
178
+ };
179
+ sdk?: {
180
+ name: string;
181
+ version: string;
182
+ };
183
+ timestamp?: string;
184
+ release?: string;
185
+ environment?: string;
186
+ breadcrumbs?: Breadcrumb[];
187
+ extra?: Record<string, unknown>;
188
+ /** Session ID for correlation with Web Vitals. */
189
+ session_id?: string;
190
+ kind?: TelemetryKind;
191
+ event_name?: string;
192
+ distinct_id?: string;
193
+ visitor?: VisitorContext;
194
+ analytics?: AnalyticsContext;
195
+ page?: PageContext;
196
+ session?: SessionContext;
197
+ trace?: TraceContext;
198
+ tool_call?: ToolCallContext;
199
+ llm?: LlmContext;
200
+ attributes?: JsonObject;
201
+ measurements?: TelemetryMeasurement[];
202
+ }
203
+ export interface IngestBatchPayload {
204
+ events: BrowserErrorEvent[];
205
+ }
206
+ export interface StackFrame {
207
+ filename?: string;
208
+ function?: string;
209
+ lineno?: number;
210
+ colno?: number;
211
+ in_app?: boolean;
212
+ context_line?: string;
213
+ pre_context?: string[];
214
+ post_context?: string[];
215
+ }
216
+ export interface UserContext {
217
+ id?: string;
218
+ email?: string;
219
+ username?: string;
220
+ ip_address?: string;
221
+ }
222
+ export interface Breadcrumb {
223
+ timestamp?: string;
224
+ category?: string;
225
+ message?: string;
226
+ level?: string;
227
+ data?: Record<string, unknown>;
228
+ }
229
+ export interface IngestResponse {
230
+ id: string;
231
+ status: string;
232
+ accepted?: number;
233
+ }
234
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @squasher-ai/browser — Type definitions for the browser SDK.
3
+ *
4
+ * Defines its own types to avoid runtime dependency on @squasher-ai/node or
5
+ * @squasher-ai/api-spec. Shapes match the canonical wire protocol in
6
+ * packages/sdk-spec/protocol.schema.json.
7
+ */
8
+ export {};
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Core Web Vitals collector.
3
+ *
4
+ * Uses Google's `web-vitals` library to measure:
5
+ * - LCP (Largest Contentful Paint)
6
+ * - CLS (Cumulative Layout Shift)
7
+ * - INP (Interaction to Next Paint)
8
+ * - FCP (First Contentful Paint)
9
+ * - TTFB (Time to First Byte)
10
+ *
11
+ * Each metric fires once (LCP, FCP, TTFB) or accumulates (CLS, INP).
12
+ * Measurements are mapped to VitalEvent and queued via the transport.
13
+ */
14
+ import type { Transport } from "./transport";
15
+ export interface VitalsCollectorConfig {
16
+ /** Sampling rate 0-1 for vitals. */
17
+ sampleRate: number;
18
+ /** Environment tag from SDK config. */
19
+ environment?: string;
20
+ /** Release tag from SDK config. */
21
+ release?: string;
22
+ /** Enable debug logging. */
23
+ debug: boolean;
24
+ }
25
+ /**
26
+ * Start collecting all 5 Core Web Vitals and feed them to the transport.
27
+ * Call once during SDK initialization.
28
+ */
29
+ export declare function startVitalsCollection(transport: Transport, config: VitalsCollectorConfig): void;
30
+ //# sourceMappingURL=vitals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitals.d.ts","sourceRoot":"","sources":["../src/vitals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,WAAW,qBAAqB;IACpC,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,KAAK,EAAE,OAAO,CAAC;CAChB;AAsDD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAuB/F"}