@squasher-ai/browser 0.1.0 → 0.2.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 (97) hide show
  1. package/LICENSE +21 -21
  2. package/dist/__tests__/client/harness.d.ts +18 -0
  3. package/dist/__tests__/client/harness.d.ts.map +1 -0
  4. package/dist/__tests__/client/harness.js +106 -0
  5. package/dist/__tests__/replay/coverage-harness.d.ts +29 -0
  6. package/dist/__tests__/replay/coverage-harness.d.ts.map +1 -0
  7. package/dist/__tests__/replay/coverage-harness.js +123 -0
  8. package/dist/__tests__/replay/test-helpers.d.ts +13 -0
  9. package/dist/__tests__/replay/test-helpers.d.ts.map +1 -0
  10. package/dist/__tests__/replay/test-helpers.js +9 -0
  11. package/dist/autocapture.d.ts +14 -18
  12. package/dist/autocapture.d.ts.map +1 -1
  13. package/dist/autocapture.js +77 -95
  14. package/dist/client.d.ts +12 -19
  15. package/dist/client.d.ts.map +1 -1
  16. package/dist/client.js +56 -72
  17. package/dist/config.d.ts +25 -0
  18. package/dist/config.d.ts.map +1 -0
  19. package/dist/config.js +26 -0
  20. package/dist/console-capture.d.ts +8 -0
  21. package/dist/console-capture.d.ts.map +1 -0
  22. package/dist/console-capture.js +79 -0
  23. package/dist/errors.d.ts +2 -2
  24. package/dist/errors.d.ts.map +1 -1
  25. package/dist/errors.js +8 -5
  26. package/dist/index.d.ts +5 -38
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +4 -82
  29. package/dist/next.d.ts +40 -0
  30. package/dist/next.d.ts.map +1 -0
  31. package/dist/next.js +67 -0
  32. package/dist/react.d.ts.map +1 -1
  33. package/dist/react.js +12 -10
  34. package/dist/replay-network.d.ts +21 -0
  35. package/dist/replay-network.d.ts.map +1 -0
  36. package/dist/replay-network.js +193 -0
  37. package/dist/replay-privacy.d.ts +2 -2
  38. package/dist/replay-privacy.d.ts.map +1 -1
  39. package/dist/replay-privacy.js +47 -17
  40. package/dist/replay-retry.d.ts +41 -0
  41. package/dist/replay-retry.d.ts.map +1 -0
  42. package/dist/replay-retry.js +193 -0
  43. package/dist/replay.d.ts +18 -5
  44. package/dist/replay.d.ts.map +1 -1
  45. package/dist/replay.js +131 -71
  46. package/dist/runtime/client-singleton.d.ts +25 -0
  47. package/dist/runtime/client-singleton.d.ts.map +1 -0
  48. package/dist/runtime/client-singleton.js +66 -0
  49. package/dist/runtime/client.d.ts +22 -0
  50. package/dist/runtime/client.d.ts.map +1 -0
  51. package/dist/runtime/client.js +12 -0
  52. package/dist/runtime/replay.d.ts +13 -0
  53. package/dist/runtime/replay.d.ts.map +1 -0
  54. package/dist/runtime/replay.js +15 -0
  55. package/dist/session.d.ts +0 -10
  56. package/dist/session.d.ts.map +1 -1
  57. package/dist/session.js +20 -21
  58. package/dist/telemetry.d.ts +1 -1
  59. package/dist/telemetry.d.ts.map +1 -1
  60. package/dist/telemetry.js +13 -10
  61. package/dist/trace-context.d.ts +9 -0
  62. package/dist/trace-context.d.ts.map +1 -0
  63. package/dist/trace-context.js +13 -0
  64. package/dist/transport.d.ts +11 -19
  65. package/dist/transport.d.ts.map +1 -1
  66. package/dist/transport.js +86 -66
  67. package/dist/types.d.ts +37 -135
  68. package/dist/types.d.ts.map +1 -1
  69. package/dist/types.js +0 -7
  70. package/dist/vitals.d.ts +55 -2
  71. package/dist/vitals.d.ts.map +1 -1
  72. package/dist/vitals.js +115 -16
  73. package/package.json +17 -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
package/dist/types.d.ts CHANGED
@@ -5,12 +5,13 @@
5
5
  * @squasher-ai/api-spec. Shapes match the canonical wire protocol in
6
6
  * packages/sdk-spec/protocol.schema.json.
7
7
  */
8
+ import type { AnalyticsContext, Breadcrumb as ApiBreadcrumb, IngestEvent, IngestResponse, JsonObject, Level, LlmContext, PageContext, SessionContext, StackFrame, TelemetryKind, TelemetryMeasurement, TelemetrySamplingConfig, ToolCallContext, TraceContext, UserContext, VisitorContext } from "@squasher-ai/api-spec";
8
9
  export interface BrowserConfig {
9
10
  /** API key (sq_pk_...). Required. */
10
11
  apiKey: string;
11
12
  /** Project ID (UUID). Required. */
12
13
  projectId: string;
13
- /** Ingestion endpoint. Default: "https://ingest.squasher.ai" */
14
+ /** Public edge ingestion endpoint. Default: "https://ingest.squasher.ai" */
14
15
  endpoint?: string;
15
16
  /** Environment tag (production, staging, development). */
16
17
  environment?: string;
@@ -18,8 +19,8 @@ export interface BrowserConfig {
18
19
  release?: string;
19
20
  /** Enable debug logging to console. Default: false. */
20
21
  debug?: boolean;
21
- /** Error sampling rate 0-1. Default: 1 (capture everything). */
22
- sampleRate?: number;
22
+ /** Outcome-aware, deterministic event sampling. */
23
+ sampling?: TelemetrySamplingConfig;
23
24
  /** Web Vitals sampling rate 0-1. Default: 1. */
24
25
  vitalsSampleRate?: number;
25
26
  /** Enable Core Web Vitals collection. Default: true. */
@@ -28,6 +29,8 @@ export interface BrowserConfig {
28
29
  enableErrorCapture?: boolean;
29
30
  /** Enable auto-capture breadcrumbs (navigation, clicks, fetch). Default: true. */
30
31
  enableAutoBreadcrumbs?: boolean;
32
+ /** Capture `console.error(...)` calls as error events. Default: false. */
33
+ captureConsoleErrors?: boolean;
31
34
  /**
32
35
  * Hook invoked before every error event is sent.
33
36
  * Return the event (possibly modified) to send, or null to drop.
@@ -39,11 +42,11 @@ export interface BrowserConfig {
39
42
  vitalsBufferSize?: number;
40
43
  /** Vitals flush interval in ms. Default: 10000. */
41
44
  vitalsFlushIntervalMs?: number;
42
- /** Optional session replay recording powered by rrweb. */
45
+ /** Optional session replay recording powered by rrweb. Enabled by default. */
43
46
  replay?: ReplayConfig;
44
47
  }
45
48
  export interface ReplayConfig {
46
- /** Enable rrweb session replay recording for this browser session. */
49
+ /** Enable rrweb session replay recording for this browser session. Default: true. */
47
50
  enabled: boolean;
48
51
  /** Session-level replay sampling rate from 0-1. Default: 1. */
49
52
  sampleRate?: number;
@@ -56,7 +59,13 @@ export interface ReplayPrivacyConfig {
56
59
  /** CSS selector for elements that should render as blocked placeholders. */
57
60
  blockSelector?: string;
58
61
  }
59
- export type VitalMetricName = "LCP" | "CLS" | "INP" | "FCP" | "TTFB";
62
+ /**
63
+ * Well-known web vital names. Includes the 5 Core Web Vitals plus the
64
+ * Next.js framework metrics emitted by `useReportWebVitals` from
65
+ * `next/web-vitals`. The wire format accepts arbitrary strings, so custom
66
+ * names from other frameworks pass through unchanged.
67
+ */
68
+ export type VitalMetricName = "LCP" | "CLS" | "INP" | "FCP" | "TTFB" | "Next.js-hydration" | "Next.js-route-change-to-render" | "Next.js-render" | (string & {});
60
69
  export type VitalRating = "good" | "needs-improvement" | "poor";
61
70
  export type DeviceType = "mobile" | "tablet" | "desktop";
62
71
  /** Single vital measurement sent to POST /v1/vitals/{project_id}. */
@@ -90,145 +99,38 @@ export interface VitalEvent {
90
99
  device_type?: DeviceType;
91
100
  /** Document referrer. */
92
101
  referrer?: string;
102
+ /** Browser language, for example en-US. */
103
+ language?: string;
104
+ /** Browser time zone, for example America/Los_Angeles. */
105
+ timezone?: string;
106
+ /** URL pathname at measurement time. */
107
+ page_path?: string;
108
+ /** URL query string without the leading question mark. */
109
+ query_string?: string;
110
+ /** UTM source query parameter. */
111
+ utm_source?: string;
112
+ /** UTM medium query parameter. */
113
+ utm_medium?: string;
114
+ /** UTM campaign query parameter. */
115
+ utm_campaign?: string;
93
116
  }
94
117
  /** Batched payload sent to the vitals endpoint. */
95
118
  export interface VitalsPayload {
96
119
  events: VitalEvent[];
97
120
  }
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;
121
+ export type { AnalyticsContext, IngestResponse, JsonObject, Level, LlmContext, PageContext, SessionContext, StackFrame, TelemetryKind, TelemetryMeasurement, ToolCallContext, TraceContext, UserContext, VisitorContext, };
122
+ export type Breadcrumb = Omit<ApiBreadcrumb, "data"> & {
123
+ data?: JsonObject;
124
+ };
125
+ export type BrowserErrorEvent = Omit<IngestEvent, "breadcrumbs" | "extra" | "request"> & {
126
+ breadcrumbs?: Breadcrumb[];
127
+ extra?: JsonObject;
175
128
  request?: {
176
129
  url: string;
177
130
  method?: string;
178
131
  };
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
- }
132
+ };
203
133
  export interface IngestBatchPayload {
204
134
  events: BrowserErrorEvent[];
205
135
  }
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
136
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
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,54 @@ 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;
82
+ export declare function startVitalsCollection(transport: VitalsTransport, config: VitalsCollectorConfig, runtime?: VitalsRuntime): VitalsCollectorHandle;
30
83
  //# sourceMappingURL=vitals.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"vitals.d.ts","sourceRoot":"","sources":["../src/vitals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAsC,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAG7E,OAAO,KAAK,EAAc,UAAU,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,SAAS;QACjB,UAAU,CAAC,EAAE;YAAE,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KACzC;CACF;AAED,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;AA4GD;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,qBAAqB,EAC7B,KAAK,CAAC,EAAE,MAAM,GACb,UAAU,CA0BZ;AAcD,MAAM,WAAW,qBAAqB;IACpC;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,IAAI,MAAM,CAAC;IACvB,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,EAAE,OAAO,EAAE;QAAE,gBAAgB,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7F,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IACrD,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,EAAE,OAAO,EAAE;QAAE,gBAAgB,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7F,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IACrD,WAAW,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;CACvD;AAED,eAAO,MAAM,oBAAoB,EAAE,aAOlC,CAAC;AAEF;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,eAAe,EAC1B,MAAM,EAAE,qBAAqB,EAC7B,OAAO,GAAE,aAAoC,GAC5C,qBAAqB,CA+BvB"}
package/dist/vitals.js CHANGED
@@ -12,15 +12,15 @@
12
12
  * Measurements are mapped to VitalEvent and queued via the transport.
13
13
  */
14
14
  import { onLCP, onCLS, onINP, onFCP, onTTFB } from "web-vitals";
15
- import { getSessionId } from "./session";
15
+ import { getSessionId } from "./session.js";
16
16
  /**
17
17
  * Infer device type from screen width.
18
18
  * Mobile < 768px, Tablet < 1024px, Desktop >= 1024px.
19
19
  */
20
20
  function inferDeviceType() {
21
- if (typeof screen === "undefined")
21
+ if (!globalThis.screen)
22
22
  return undefined;
23
- const width = screen.width;
23
+ const width = globalThis.screen.width;
24
24
  if (width < 768)
25
25
  return "mobile";
26
26
  if (width < 1024)
@@ -32,13 +32,54 @@ function inferDeviceType() {
32
32
  * Returns undefined if not supported (Firefox, Safari).
33
33
  */
34
34
  function getConnectionType() {
35
- const nav = navigator;
36
- return nav.connection?.effectiveType;
35
+ return globalThis.navigator?.connection?.effectiveType;
36
+ }
37
+ function getBrowserTimezone() {
38
+ try {
39
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
40
+ }
41
+ catch {
42
+ return undefined;
43
+ }
44
+ }
45
+ function getUrlDimensions() {
46
+ if (!globalThis.location) {
47
+ return {
48
+ page_path: "",
49
+ query_string: "",
50
+ url: "",
51
+ utm_campaign: undefined,
52
+ utm_medium: undefined,
53
+ utm_source: undefined,
54
+ };
55
+ }
56
+ const href = globalThis.location.href;
57
+ const parsed = href ? safeUrl(href) : undefined;
58
+ const rawSearch = globalThis.location.search || parsed?.search || "";
59
+ const search = rawSearch.startsWith("?") ? rawSearch.slice(1) : rawSearch;
60
+ const params = new URLSearchParams(search);
61
+ return {
62
+ page_path: globalThis.location.pathname || parsed?.pathname || "",
63
+ query_string: search,
64
+ url: href,
65
+ utm_campaign: params.get("utm_campaign") || undefined,
66
+ utm_medium: params.get("utm_medium") || undefined,
67
+ utm_source: params.get("utm_source") || undefined,
68
+ };
69
+ }
70
+ function safeUrl(value) {
71
+ try {
72
+ return new URL(value);
73
+ }
74
+ catch {
75
+ return undefined;
76
+ }
37
77
  }
38
78
  /**
39
79
  * Map a web-vitals Metric to our VitalEvent shape.
40
80
  */
41
- function mapMetric(metric, config) {
81
+ function mapMetric(metric, config, getCurrentSessionId) {
82
+ const url = getUrlDimensions();
42
83
  return {
43
84
  type: "vital",
44
85
  name: metric.name,
@@ -46,16 +87,59 @@ function mapMetric(metric, config) {
46
87
  rating: metric.rating,
47
88
  delta: metric.delta,
48
89
  navigationType: metric.navigationType,
49
- url: location.href,
90
+ url: url.url,
91
+ session_id: getCurrentSessionId(),
92
+ timestamp: Date.now(),
93
+ environment: config.environment,
94
+ release: config.release,
95
+ connection: getConnectionType(),
96
+ device_type: inferDeviceType(),
97
+ language: globalThis.navigator?.language || undefined,
98
+ page_path: url.page_path,
99
+ query_string: url.query_string,
100
+ referrer: globalThis.document?.referrer || undefined,
101
+ timezone: getBrowserTimezone(),
102
+ utm_campaign: url.utm_campaign,
103
+ utm_medium: url.utm_medium,
104
+ utm_source: url.utm_source,
105
+ };
106
+ }
107
+ /**
108
+ * Map an externally-sourced web vital (Next.js custom metric or otherwise)
109
+ * to our VitalEvent wire shape so it can be enqueued through the transport.
110
+ */
111
+ export function mapExternalVital(metric, config, route) {
112
+ const url = getUrlDimensions();
113
+ return {
114
+ type: "vital",
115
+ name: metric.name,
116
+ value: metric.value,
117
+ rating: normalizeRating(metric.rating),
118
+ delta: metric.delta ?? metric.value,
119
+ navigationType: metric.navigationType ?? "navigate",
120
+ url: url.url,
121
+ route,
50
122
  session_id: getSessionId(),
51
123
  timestamp: Date.now(),
52
124
  environment: config.environment,
53
125
  release: config.release,
54
126
  connection: getConnectionType(),
55
127
  device_type: inferDeviceType(),
56
- referrer: document.referrer || undefined,
128
+ language: globalThis.navigator?.language || undefined,
129
+ page_path: url.page_path,
130
+ query_string: url.query_string,
131
+ referrer: globalThis.document?.referrer || undefined,
132
+ timezone: getBrowserTimezone(),
133
+ utm_campaign: url.utm_campaign,
134
+ utm_medium: url.utm_medium,
135
+ utm_source: url.utm_source,
57
136
  };
58
137
  }
138
+ function normalizeRating(rating) {
139
+ if (rating === "poor" || rating === "needs-improvement")
140
+ return rating;
141
+ return "good";
142
+ }
59
143
  /** Decide whether to sample this vital based on the configured rate. */
60
144
  function shouldSample(sampleRate) {
61
145
  if (sampleRate >= 1)
@@ -64,19 +148,33 @@ function shouldSample(sampleRate) {
64
148
  return false;
65
149
  return Math.random() < sampleRate;
66
150
  }
151
+ export const defaultVitalsRuntime = {
152
+ getSessionId,
153
+ observeCLS: (callback, options) => onCLS(callback, options),
154
+ observeFCP: (callback) => onFCP(callback),
155
+ observeINP: (callback, options) => onINP(callback, options),
156
+ observeLCP: (callback) => onLCP(callback),
157
+ observeTTFB: (callback) => onTTFB(callback),
158
+ };
67
159
  /**
68
160
  * Start collecting all 5 Core Web Vitals and feed them to the transport.
69
161
  * Call once during SDK initialization.
70
162
  */
71
- export function startVitalsCollection(transport, config) {
163
+ export function startVitalsCollection(transport, config, runtime = defaultVitalsRuntime) {
164
+ let suppressed = false;
165
+ const suppress = () => {
166
+ suppressed = true;
167
+ };
72
168
  if (!shouldSample(config.sampleRate)) {
73
169
  if (config.debug) {
74
170
  console.log("[squasher] Vitals collection skipped (sampled out)");
75
171
  }
76
- return;
172
+ return { suppress };
77
173
  }
78
174
  const handle = (metric) => {
79
- const event = mapMetric(metric, config);
175
+ if (suppressed)
176
+ return;
177
+ const event = mapMetric(metric, config, runtime.getSessionId);
80
178
  if (config.debug) {
81
179
  console.log(`[squasher] ${metric.name}: ${metric.value.toFixed(2)} (${metric.rating})`);
82
180
  }
@@ -84,9 +182,10 @@ export function startVitalsCollection(transport, config) {
84
182
  };
85
183
  // Register all 5 metric observers.
86
184
  // reportAllChanges: true for CLS/INP so we get cumulative values.
87
- onLCP(handle);
88
- onCLS(handle, { reportAllChanges: true });
89
- onINP(handle, { reportAllChanges: true });
90
- onFCP(handle);
91
- onTTFB(handle);
185
+ runtime.observeLCP(handle);
186
+ runtime.observeCLS(handle, { reportAllChanges: true });
187
+ runtime.observeINP(handle, { reportAllChanges: true });
188
+ runtime.observeFCP(handle);
189
+ runtime.observeTTFB(handle);
190
+ return { suppress };
92
191
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@squasher-ai/browser",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Official @squasher-ai/browser package for Squasher.",
5
5
  "homepage": "https://squasher.ai",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/squasher-ai/squasher.git",
10
- "directory": "packages/sdk-browser"
10
+ "directory": "packages/public/sdk-browser"
11
11
  },
12
12
  "bugs": {
13
13
  "url": "https://github.com/squasher-ai/squasher/issues"
@@ -20,23 +20,37 @@
20
20
  "types": "./dist/index.d.ts",
21
21
  "exports": {
22
22
  ".": {
23
+ "source": "./src/index.ts",
23
24
  "import": "./dist/index.js",
24
25
  "types": "./dist/index.d.ts"
25
26
  },
26
27
  "./react": {
28
+ "source": "./src/react.tsx",
27
29
  "import": "./dist/react.js",
28
30
  "types": "./dist/react.d.ts"
31
+ },
32
+ "./next": {
33
+ "source": "./src/next.tsx",
34
+ "import": "./dist/next.js",
35
+ "types": "./dist/next.d.ts"
29
36
  }
30
37
  },
31
38
  "sideEffects": false,
32
39
  "dependencies": {
33
- "rrweb": "^2.0.0-alpha.4",
40
+ "@squasher-ai/api-spec": "0.2.0",
41
+ "@squasher-ai/result-utils": "0.2.0",
42
+ "@squasher-ai/sdk-core": "0.2.0",
43
+ "rrweb": "^2.0.0-alpha.20",
34
44
  "web-vitals": "^4.2.4"
35
45
  },
36
46
  "peerDependencies": {
47
+ "next": ">=14",
37
48
  "react": ">=18"
38
49
  },
39
50
  "peerDependenciesMeta": {
51
+ "next": {
52
+ "optional": true
53
+ },
40
54
  "react": {
41
55
  "optional": true
42
56
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=client.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/client.test.ts"],"names":[],"mappings":""}