@squasher-ai/browser 0.1.1 → 0.3.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 (109) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +3 -42
  3. package/dist/_vendor/result-runtime/attempt.d.ts +17 -0
  4. package/dist/_vendor/result-runtime/attempt.js +62 -0
  5. package/dist/_vendor/sdk-runtime/batching/index.d.ts +42 -0
  6. package/dist/_vendor/sdk-runtime/batching/index.js +48 -0
  7. package/dist/_vendor/sdk-runtime/errors/headers.d.ts +6 -0
  8. package/dist/_vendor/sdk-runtime/errors/headers.js +44 -0
  9. package/dist/_vendor/sdk-runtime/errors/index.d.ts +95 -0
  10. package/dist/_vendor/sdk-runtime/errors/index.js +157 -0
  11. package/dist/_vendor/sdk-runtime/headers.d.ts +48 -0
  12. package/dist/_vendor/sdk-runtime/headers.js +67 -0
  13. package/dist/_vendor/sdk-runtime/platform.d.ts +33 -0
  14. package/dist/_vendor/sdk-runtime/platform.js +173 -0
  15. package/dist/_vendor/sdk-runtime/retry.d.ts +50 -0
  16. package/dist/_vendor/sdk-runtime/retry.js +104 -0
  17. package/dist/_vendor/sdk-runtime/runtime/actionable-error.d.ts +37 -0
  18. package/dist/_vendor/sdk-runtime/runtime/actionable-error.js +123 -0
  19. package/dist/_vendor/sdk-runtime/runtime/environment.d.ts +18 -0
  20. package/dist/_vendor/sdk-runtime/runtime/environment.js +81 -0
  21. package/dist/_vendor/sdk-runtime/runtime/public-sdk-runtime.d.ts +12 -0
  22. package/dist/_vendor/sdk-runtime/runtime/public-sdk-runtime.js +38 -0
  23. package/dist/_vendor/sdk-runtime/runtime/redaction.d.ts +14 -0
  24. package/dist/_vendor/sdk-runtime/runtime/redaction.js +120 -0
  25. package/dist/_vendor/sdk-runtime/runtime/release.d.ts +35 -0
  26. package/dist/_vendor/sdk-runtime/runtime/release.js +120 -0
  27. package/dist/_vendor/sdk-runtime/sampling/index.d.ts +43 -0
  28. package/dist/_vendor/sdk-runtime/sampling/index.js +70 -0
  29. package/dist/_vendor/telemetry-contract/sdk/public-contract.d.ts +175 -0
  30. package/dist/_vendor/telemetry-contract/sdk/public-contract.js +5 -0
  31. package/dist/autocapture.d.ts +14 -19
  32. package/dist/autocapture.js +77 -95
  33. package/dist/client.d.ts +12 -20
  34. package/dist/client.js +56 -72
  35. package/dist/config.d.ts +24 -0
  36. package/dist/config.js +26 -0
  37. package/dist/console-capture.d.ts +7 -0
  38. package/dist/console-capture.js +79 -0
  39. package/dist/errors.d.ts +2 -3
  40. package/dist/errors.js +8 -5
  41. package/dist/index.d.ts +5 -39
  42. package/dist/index.js +4 -82
  43. package/dist/next.d.ts +39 -0
  44. package/dist/next.js +67 -0
  45. package/dist/react.d.ts +0 -1
  46. package/dist/react.js +12 -10
  47. package/dist/replay-network.d.ts +20 -0
  48. package/dist/replay-network.js +193 -0
  49. package/dist/replay-privacy.d.ts +2 -3
  50. package/dist/replay-privacy.js +47 -17
  51. package/dist/replay-retry.d.ts +40 -0
  52. package/dist/replay-retry.js +193 -0
  53. package/dist/replay.d.ts +18 -6
  54. package/dist/replay.js +131 -71
  55. package/dist/runtime/client-singleton.d.ts +24 -0
  56. package/dist/runtime/client-singleton.js +66 -0
  57. package/dist/runtime/client.d.ts +21 -0
  58. package/dist/runtime/client.js +12 -0
  59. package/dist/runtime/replay.d.ts +12 -0
  60. package/dist/runtime/replay.js +15 -0
  61. package/dist/session.d.ts +0 -11
  62. package/dist/session.js +20 -21
  63. package/dist/telemetry.d.ts +1 -2
  64. package/dist/telemetry.js +13 -10
  65. package/dist/trace-context.d.ts +8 -0
  66. package/dist/trace-context.js +13 -0
  67. package/dist/transport.d.ts +11 -20
  68. package/dist/transport.js +86 -66
  69. package/dist/types.d.ts +38 -139
  70. package/dist/types.js +0 -7
  71. package/dist/vitals.d.ts +55 -3
  72. package/dist/vitals.js +115 -16
  73. package/package.json +11 -3
  74. package/dist/__tests__/client.test.d.ts +0 -2
  75. package/dist/__tests__/client.test.d.ts.map +0 -1
  76. package/dist/__tests__/client.test.js +0 -103
  77. package/dist/__tests__/errors.test.d.ts +0 -2
  78. package/dist/__tests__/errors.test.d.ts.map +0 -1
  79. package/dist/__tests__/errors.test.js +0 -80
  80. package/dist/__tests__/replay-privacy.test.d.ts +0 -2
  81. package/dist/__tests__/replay-privacy.test.d.ts.map +0 -1
  82. package/dist/__tests__/replay-privacy.test.js +0 -124
  83. package/dist/__tests__/replay-startup.test.d.ts +0 -2
  84. package/dist/__tests__/replay-startup.test.d.ts.map +0 -1
  85. package/dist/__tests__/replay-startup.test.js +0 -99
  86. package/dist/__tests__/replay.test.d.ts +0 -2
  87. package/dist/__tests__/replay.test.d.ts.map +0 -1
  88. package/dist/__tests__/replay.test.js +0 -217
  89. package/dist/__tests__/session.test.d.ts +0 -2
  90. package/dist/__tests__/session.test.d.ts.map +0 -1
  91. package/dist/__tests__/session.test.js +0 -46
  92. package/dist/__tests__/transport.test.d.ts +0 -2
  93. package/dist/__tests__/transport.test.d.ts.map +0 -1
  94. package/dist/__tests__/transport.test.js +0 -101
  95. package/dist/__tests__/types.test.d.ts +0 -2
  96. package/dist/__tests__/types.test.d.ts.map +0 -1
  97. package/dist/__tests__/types.test.js +0 -139
  98. package/dist/autocapture.d.ts.map +0 -1
  99. package/dist/client.d.ts.map +0 -1
  100. package/dist/errors.d.ts.map +0 -1
  101. package/dist/index.d.ts.map +0 -1
  102. package/dist/react.d.ts.map +0 -1
  103. package/dist/replay-privacy.d.ts.map +0 -1
  104. package/dist/replay.d.ts.map +0 -1
  105. package/dist/session.d.ts.map +0 -1
  106. package/dist/telemetry.d.ts.map +0 -1
  107. package/dist/transport.d.ts.map +0 -1
  108. package/dist/types.d.ts.map +0 -1
  109. package/dist/vitals.d.ts.map +0 -1
@@ -1,19 +1,14 @@
1
1
  /**
2
- * Transport layer for the browser SDK.
2
+ * Browser transport.
3
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)
4
+ * - Errors → POST /v1/ingest/{project_id} (per-event, immediate).
5
+ * - Vitals → POST /v1/vitals/{project_id} (batched).
7
6
  *
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)
7
+ * Uses navigator.sendBeacon when available so requests survive page unload,
8
+ * falling back to `fetch({ keepalive: true })`. Vitals flush on buffer full,
9
+ * interval tick, and page visibilitychange/pagehide.
15
10
  */
16
- import type { BrowserErrorEvent, VitalEvent } from "./types";
11
+ import type { BrowserErrorEvent, VitalEvent } from "./types.js";
17
12
  export interface TransportConfig {
18
13
  endpoint: string;
19
14
  projectId: string;
@@ -28,20 +23,16 @@ export declare class Transport {
28
23
  private flushTimer;
29
24
  private disposed;
30
25
  constructor(config: TransportConfig);
31
- /** Send a single telemetry event immediately. Fire-and-forget. */
26
+ /** Fire-and-forget. */
32
27
  sendEvent(event: BrowserErrorEvent): void;
33
- /** Backward-compatible alias for error events. */
28
+ /** @deprecated use {@link sendEvent}. */
34
29
  sendError(event: BrowserErrorEvent): void;
35
- /** Buffer a vital event. Auto-flushes when buffer is full. */
36
30
  enqueueVital(event: VitalEvent): void;
37
- /** Flush all buffered vitals immediately. */
38
31
  flushVitals(): void;
39
- /** Stop all timers and flush remaining data. */
40
32
  dispose(): void;
41
33
  private buildIngestUrl;
42
- /** Try sending via navigator.sendBeacon. Returns true if successful. */
34
+ private buildVitalsUrl;
35
+ private buildUrl;
43
36
  private trySendBeacon;
44
- /** Send via fetch with keepalive and the API key header. */
45
37
  private sendFetch;
46
38
  }
47
- //# sourceMappingURL=transport.d.ts.map
package/dist/transport.js CHANGED
@@ -1,18 +1,31 @@
1
1
  /**
2
- * Transport layer for the browser SDK.
2
+ * Browser transport.
3
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)
4
+ * - Errors → POST /v1/ingest/{project_id} (per-event, immediate).
5
+ * - Vitals → POST /v1/vitals/{project_id} (batched).
7
6
  *
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)
7
+ * Uses navigator.sendBeacon when available so requests survive page unload,
8
+ * falling back to `fetch({ keepalive: true })`. Vitals flush on buffer full,
9
+ * interval tick, and page visibilitychange/pagehide.
15
10
  */
11
+ import { attempt, attemptAsync } from "./_vendor/result-runtime/attempt.js";
12
+ import { getDefaultHeaders } from "./_vendor/sdk-runtime/runtime/public-sdk-runtime.js";
13
+ const SDK_NAME = "@squasher-ai/browser";
14
+ const SDK_VERSION = "0.3.0";
15
+ const BEACON_CONTENT_TYPE = "text/plain";
16
+ // One allocation per process; getDefaultHeaders is pure given package
17
+ // metadata and platform detection memoizes inside sdk-core.
18
+ const DEFAULT_TELEMETRY_HEADERS = getDefaultHeaders({
19
+ packageName: SDK_NAME,
20
+ packageVersion: SDK_VERSION,
21
+ });
22
+ function normalizeEndpoint(endpoint) {
23
+ const trimmed = endpoint.trim();
24
+ if (trimmed.length === 0) {
25
+ return null;
26
+ }
27
+ return trimmed.replace(/\/+$/, "");
28
+ }
16
29
  export class Transport {
17
30
  config;
18
31
  vitalsBuffer = [];
@@ -20,50 +33,47 @@ export class Transport {
20
33
  disposed = false;
21
34
  constructor(config) {
22
35
  this.config = config;
23
- // Start the vitals flush timer
24
36
  this.flushTimer = setInterval(() => {
25
37
  this.flushVitals();
26
38
  }, 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") {
39
+ if (globalThis.document) {
40
+ globalThis.document.addEventListener("visibilitychange", () => {
41
+ if (globalThis.document.visibilityState === "hidden") {
31
42
  this.flushVitals();
32
43
  }
33
44
  });
34
45
  }
35
- // Also flush on pagehide (more reliable than unload)
36
- if (typeof window !== "undefined") {
37
- window.addEventListener("pagehide", () => {
46
+ // `pagehide` fires in cases where `unload` does not (bfcache, iOS Safari).
47
+ if (globalThis.window) {
48
+ globalThis.window.addEventListener("pagehide", () => {
38
49
  this.flushVitals();
39
50
  });
40
51
  }
41
52
  }
42
- // ─── Error sending (immediate) ──────────────────────────────────────
43
- /** Send a single telemetry event immediately. Fire-and-forget. */
53
+ /** Fire-and-forget. */
44
54
  sendEvent(event) {
45
55
  if (this.disposed)
46
56
  return;
47
57
  const url = this.buildIngestUrl();
58
+ const beaconUrl = this.buildIngestUrl({ beacon: true });
59
+ if (!url || !beaconUrl) {
60
+ return;
61
+ }
48
62
  const body = JSON.stringify(event);
49
- // Try sendBeacon first (survives page unload)
50
- if (this.trySendBeacon(this.buildIngestUrl({ beacon: true }), body)) {
63
+ if (this.trySendBeacon(beaconUrl, body)) {
51
64
  if (this.config.debug) {
52
65
  console.log("[squasher] Error sent via sendBeacon");
53
66
  }
54
67
  return;
55
68
  }
56
- // Fallback to fetch with keepalive
57
69
  this.sendFetch(url, body).catch(() => {
58
- // Fire-and-forget — errors are best-effort in the browser
70
+ // Best-effort in the browser.
59
71
  });
60
72
  }
61
- /** Backward-compatible alias for error events. */
73
+ /** @deprecated use {@link sendEvent}. */
62
74
  sendError(event) {
63
75
  this.sendEvent(event);
64
76
  }
65
- // ─── Vitals buffering ───────────────────────────────────────────────
66
- /** Buffer a vital event. Auto-flushes when buffer is full. */
67
77
  enqueueVital(event) {
68
78
  if (this.disposed)
69
79
  return;
@@ -72,25 +82,25 @@ export class Transport {
72
82
  this.flushVitals();
73
83
  }
74
84
  }
75
- /** Flush all buffered vitals immediately. */
76
85
  flushVitals() {
77
86
  if (this.vitalsBuffer.length === 0)
78
87
  return;
88
+ const url = this.buildVitalsUrl();
89
+ const beaconUrl = this.buildVitalsUrl({ beacon: true });
90
+ if (!url || !beaconUrl) {
91
+ return;
92
+ }
79
93
  const events = this.vitalsBuffer.splice(0);
80
- const url = `${this.config.endpoint}/v1/vitals/${this.config.projectId}`;
81
94
  const payload = { events };
82
95
  const body = JSON.stringify(payload);
83
- if (this.trySendBeacon(url, body)) {
96
+ if (this.trySendBeacon(beaconUrl, body)) {
84
97
  if (this.config.debug) {
85
98
  console.log(`[squasher] Flushed ${events.length} vitals via sendBeacon`);
86
99
  }
87
100
  return;
88
101
  }
89
- this.sendFetch(url, body).catch(() => {
90
- // Best-effort
91
- });
102
+ this.sendFetch(url, body).catch(() => { });
92
103
  }
93
- /** Stop all timers and flush remaining data. */
94
104
  dispose() {
95
105
  this.disposed = true;
96
106
  if (this.flushTimer) {
@@ -99,47 +109,57 @@ export class Transport {
99
109
  }
100
110
  this.flushVitals();
101
111
  }
102
- // ─── Private helpers ────────────────────────────────────────────────
103
112
  buildIngestUrl(options) {
104
- const baseUrl = `${this.config.endpoint}/v1/ingest/${this.config.projectId}`;
113
+ return this.buildUrl("ingest", options);
114
+ }
115
+ buildVitalsUrl(options) {
116
+ return this.buildUrl("vitals", options);
117
+ }
118
+ buildUrl(kind, options) {
119
+ const endpoint = normalizeEndpoint(this.config.endpoint);
120
+ if (!endpoint) {
121
+ return null;
122
+ }
123
+ const baseUrl = `${endpoint}/v1/${kind}/${this.config.projectId}`;
105
124
  if (!options?.beacon) {
106
125
  return baseUrl;
107
126
  }
108
127
  return `${baseUrl}?key=${encodeURIComponent(this.config.apiKey)}`;
109
128
  }
110
- /** Try sending via navigator.sendBeacon. Returns true if successful. */
111
129
  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 {
130
+ if (!globalThis.navigator?.sendBeacon) {
120
131
  return false;
121
132
  }
133
+ return attempt({
134
+ try: () => {
135
+ const blob = new Blob([body], { type: BEACON_CONTENT_TYPE });
136
+ return globalThis.navigator.sendBeacon(url, blob);
137
+ },
138
+ catch: () => false,
139
+ });
122
140
  }
123
- /** Send via fetch with keepalive and the API key header. */
124
141
  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
- }
142
+ await attemptAsync({
143
+ try: async () => {
144
+ await fetch(url, {
145
+ method: "POST",
146
+ headers: {
147
+ ...DEFAULT_TELEMETRY_HEADERS,
148
+ "Content-Type": "application/json",
149
+ "x-squasher-key": this.config.apiKey,
150
+ },
151
+ body,
152
+ keepalive: true,
153
+ });
154
+ if (this.config.debug) {
155
+ console.log("[squasher] Event sent via fetch");
156
+ }
157
+ },
158
+ catch: ({ cause }) => {
159
+ if (this.config.debug) {
160
+ console.warn("[squasher] Failed to send event:", cause);
161
+ }
162
+ },
163
+ });
144
164
  }
145
165
  }
package/dist/types.d.ts CHANGED
@@ -1,16 +1,15 @@
1
1
  /**
2
2
  * @squasher-ai/browser — Type definitions for the browser SDK.
3
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.
4
+ * Uses type-only telemetry contracts so the runtime stays browser-safe.
7
5
  */
6
+ import type { AnalyticsContext, Breadcrumb as ApiBreadcrumb, IngestEvent, IngestResponse, JsonObject, Level, LlmContext, PageContext, SessionContext, StackFrame, TelemetryKind, TelemetryMeasurement, TelemetrySamplingConfig, ToolCallContext, TraceContext, UserContext, VisitorContext } from "./_vendor/telemetry-contract/sdk/public-contract.js";
8
7
  export interface BrowserConfig {
9
8
  /** API key (sq_pk_...). Required. */
10
9
  apiKey: string;
11
10
  /** Project ID (UUID). Required. */
12
11
  projectId: string;
13
- /** Ingestion endpoint. Default: "https://ingest.squasher.ai" */
12
+ /** Public edge ingestion endpoint. Default: "https://ingest.squasher.ai" */
14
13
  endpoint?: string;
15
14
  /** Environment tag (production, staging, development). */
16
15
  environment?: string;
@@ -18,8 +17,8 @@ export interface BrowserConfig {
18
17
  release?: string;
19
18
  /** Enable debug logging to console. Default: false. */
20
19
  debug?: boolean;
21
- /** Error sampling rate 0-1. Default: 1 (capture everything). */
22
- sampleRate?: number;
20
+ /** Outcome-aware, deterministic event sampling. */
21
+ sampling?: TelemetrySamplingConfig;
23
22
  /** Web Vitals sampling rate 0-1. Default: 1. */
24
23
  vitalsSampleRate?: number;
25
24
  /** Enable Core Web Vitals collection. Default: true. */
@@ -28,6 +27,8 @@ export interface BrowserConfig {
28
27
  enableErrorCapture?: boolean;
29
28
  /** Enable auto-capture breadcrumbs (navigation, clicks, fetch). Default: true. */
30
29
  enableAutoBreadcrumbs?: boolean;
30
+ /** Capture `console.error(...)` calls as error events. Default: false. */
31
+ captureConsoleErrors?: boolean;
31
32
  /**
32
33
  * Hook invoked before every error event is sent.
33
34
  * Return the event (possibly modified) to send, or null to drop.
@@ -39,11 +40,11 @@ export interface BrowserConfig {
39
40
  vitalsBufferSize?: number;
40
41
  /** Vitals flush interval in ms. Default: 10000. */
41
42
  vitalsFlushIntervalMs?: number;
42
- /** Optional session replay recording powered by rrweb. */
43
+ /** Optional session replay recording powered by rrweb. Enabled by default. */
43
44
  replay?: ReplayConfig;
44
45
  }
45
46
  export interface ReplayConfig {
46
- /** Enable rrweb session replay recording for this browser session. */
47
+ /** Enable rrweb session replay recording for this browser session. Default: true. */
47
48
  enabled: boolean;
48
49
  /** Session-level replay sampling rate from 0-1. Default: 1. */
49
50
  sampleRate?: number;
@@ -56,7 +57,13 @@ export interface ReplayPrivacyConfig {
56
57
  /** CSS selector for elements that should render as blocked placeholders. */
57
58
  blockSelector?: string;
58
59
  }
59
- export type VitalMetricName = "LCP" | "CLS" | "INP" | "FCP" | "TTFB";
60
+ /**
61
+ * Well-known web vital names. Includes the 5 Core Web Vitals plus the
62
+ * Next.js framework metrics emitted by `useReportWebVitals` from
63
+ * `next/web-vitals`. The wire format accepts arbitrary strings, so custom
64
+ * names from other frameworks pass through unchanged.
65
+ */
66
+ export type VitalMetricName = "LCP" | "CLS" | "INP" | "FCP" | "TTFB" | "Next.js-hydration" | "Next.js-route-change-to-render" | "Next.js-render" | (string & {});
60
67
  export type VitalRating = "good" | "needs-improvement" | "poor";
61
68
  export type DeviceType = "mobile" | "tablet" | "desktop";
62
69
  /** Single vital measurement sent to POST /v1/vitals/{project_id}. */
@@ -90,145 +97,37 @@ export interface VitalEvent {
90
97
  device_type?: DeviceType;
91
98
  /** Document referrer. */
92
99
  referrer?: string;
100
+ /** Browser language, for example en-US. */
101
+ language?: string;
102
+ /** Browser time zone, for example America/Los_Angeles. */
103
+ timezone?: string;
104
+ /** URL pathname at measurement time. */
105
+ page_path?: string;
106
+ /** URL query string without the leading question mark. */
107
+ query_string?: string;
108
+ /** UTM source query parameter. */
109
+ utm_source?: string;
110
+ /** UTM medium query parameter. */
111
+ utm_medium?: string;
112
+ /** UTM campaign query parameter. */
113
+ utm_campaign?: string;
93
114
  }
94
115
  /** Batched payload sent to the vitals endpoint. */
95
116
  export interface VitalsPayload {
96
117
  events: VitalEvent[];
97
118
  }
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;
119
+ export type { AnalyticsContext, IngestResponse, JsonObject, Level, LlmContext, PageContext, SessionContext, StackFrame, TelemetryKind, TelemetryMeasurement, ToolCallContext, TraceContext, UserContext, VisitorContext, };
120
+ export type Breadcrumb = Omit<ApiBreadcrumb, "data"> & {
121
+ data?: JsonObject;
122
+ };
123
+ export type BrowserErrorEvent = Omit<IngestEvent, "breadcrumbs" | "extra" | "request"> & {
124
+ breadcrumbs?: Breadcrumb[];
125
+ extra?: JsonObject;
175
126
  request?: {
176
127
  url: string;
177
128
  method?: string;
178
129
  };
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
- }
130
+ };
203
131
  export interface IngestBatchPayload {
204
132
  events: BrowserErrorEvent[];
205
133
  }
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
package/dist/types.js CHANGED
@@ -1,8 +1 @@
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
1
  export {};
package/dist/vitals.d.ts CHANGED
@@ -11,7 +11,15 @@
11
11
  * Each metric fires once (LCP, FCP, TTFB) or accumulates (CLS, INP).
12
12
  * Measurements are mapped to VitalEvent and queued via the transport.
13
13
  */
14
- import type { Transport } from "./transport";
14
+ import { type Metric } from "web-vitals";
15
+ import type { VitalEvent } from "./types.js";
16
+ declare global {
17
+ interface Navigator {
18
+ connection?: {
19
+ effectiveType?: string;
20
+ };
21
+ }
22
+ }
15
23
  export interface VitalsCollectorConfig {
16
24
  /** Sampling rate 0-1 for vitals. */
17
25
  sampleRate: number;
@@ -22,9 +30,53 @@ export interface VitalsCollectorConfig {
22
30
  /** Enable debug logging. */
23
31
  debug: boolean;
24
32
  }
33
+ /**
34
+ * Shape accepted by external collectors that bridge into our transport
35
+ * (e.g. Next.js `useReportWebVitals`). Mirrors the relevant subset of the
36
+ * `web-vitals` Metric and Next's `NextWebVitalsMetric` so callers can pass
37
+ * the framework metric object through unchanged.
38
+ */
39
+ export interface ExternalVitalInput {
40
+ name: string;
41
+ value: number;
42
+ rating?: string;
43
+ delta?: number;
44
+ navigationType?: string;
45
+ /** Unique identifier the underlying lib assigns to a measurement. */
46
+ id?: string;
47
+ }
48
+ /**
49
+ * Map an externally-sourced web vital (Next.js custom metric or otherwise)
50
+ * to our VitalEvent wire shape so it can be enqueued through the transport.
51
+ */
52
+ export declare function mapExternalVital(metric: ExternalVitalInput, config: VitalsCollectorConfig, route?: string): VitalEvent;
53
+ export interface VitalsCollectorHandle {
54
+ /**
55
+ * Stop forwarding metrics from the native `web-vitals` library to the
56
+ * transport. The library's observers cannot be unregistered, so callbacks
57
+ * still fire — they just no-op. Use this when an external collector (e.g.
58
+ * Next.js `useReportWebVitals`) takes over so we don't double-count.
59
+ */
60
+ suppress: () => void;
61
+ }
62
+ export interface VitalsTransport {
63
+ enqueueVital(event: VitalEvent): void;
64
+ }
65
+ export interface VitalsRuntime {
66
+ getSessionId(): string;
67
+ observeCLS(callback: (metric: Metric) => void, options: {
68
+ reportAllChanges: boolean;
69
+ }): void;
70
+ observeFCP(callback: (metric: Metric) => void): void;
71
+ observeINP(callback: (metric: Metric) => void, options: {
72
+ reportAllChanges: boolean;
73
+ }): void;
74
+ observeLCP(callback: (metric: Metric) => void): void;
75
+ observeTTFB(callback: (metric: Metric) => void): void;
76
+ }
77
+ export declare const defaultVitalsRuntime: VitalsRuntime;
25
78
  /**
26
79
  * Start collecting all 5 Core Web Vitals and feed them to the transport.
27
80
  * Call once during SDK initialization.
28
81
  */
29
- export declare function startVitalsCollection(transport: Transport, config: VitalsCollectorConfig): void;
30
- //# sourceMappingURL=vitals.d.ts.map
82
+ export declare function startVitalsCollection(transport: VitalsTransport, config: VitalsCollectorConfig, runtime?: VitalsRuntime): VitalsCollectorHandle;