@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.
- package/LICENSE +21 -21
- package/dist/__tests__/client/harness.d.ts +18 -0
- package/dist/__tests__/client/harness.d.ts.map +1 -0
- package/dist/__tests__/client/harness.js +106 -0
- package/dist/__tests__/replay/coverage-harness.d.ts +29 -0
- package/dist/__tests__/replay/coverage-harness.d.ts.map +1 -0
- package/dist/__tests__/replay/coverage-harness.js +123 -0
- package/dist/__tests__/replay/test-helpers.d.ts +13 -0
- package/dist/__tests__/replay/test-helpers.d.ts.map +1 -0
- package/dist/__tests__/replay/test-helpers.js +9 -0
- package/dist/autocapture.d.ts +14 -18
- package/dist/autocapture.d.ts.map +1 -1
- package/dist/autocapture.js +77 -95
- package/dist/client.d.ts +12 -19
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +56 -72
- package/dist/config.d.ts +25 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +26 -0
- package/dist/console-capture.d.ts +8 -0
- package/dist/console-capture.d.ts.map +1 -0
- package/dist/console-capture.js +79 -0
- package/dist/errors.d.ts +2 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +8 -5
- package/dist/index.d.ts +5 -38
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -82
- package/dist/next.d.ts +40 -0
- package/dist/next.d.ts.map +1 -0
- package/dist/next.js +67 -0
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +12 -10
- package/dist/replay-network.d.ts +21 -0
- package/dist/replay-network.d.ts.map +1 -0
- package/dist/replay-network.js +193 -0
- package/dist/replay-privacy.d.ts +2 -2
- package/dist/replay-privacy.d.ts.map +1 -1
- package/dist/replay-privacy.js +47 -17
- package/dist/replay-retry.d.ts +41 -0
- package/dist/replay-retry.d.ts.map +1 -0
- package/dist/replay-retry.js +193 -0
- package/dist/replay.d.ts +18 -5
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +131 -71
- package/dist/runtime/client-singleton.d.ts +25 -0
- package/dist/runtime/client-singleton.d.ts.map +1 -0
- package/dist/runtime/client-singleton.js +66 -0
- package/dist/runtime/client.d.ts +22 -0
- package/dist/runtime/client.d.ts.map +1 -0
- package/dist/runtime/client.js +12 -0
- package/dist/runtime/replay.d.ts +13 -0
- package/dist/runtime/replay.d.ts.map +1 -0
- package/dist/runtime/replay.js +15 -0
- package/dist/session.d.ts +0 -10
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +20 -21
- package/dist/telemetry.d.ts +1 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +13 -10
- package/dist/trace-context.d.ts +9 -0
- package/dist/trace-context.d.ts.map +1 -0
- package/dist/trace-context.js +13 -0
- package/dist/transport.d.ts +11 -19
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +86 -66
- package/dist/types.d.ts +37 -135
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -7
- package/dist/vitals.d.ts +55 -2
- package/dist/vitals.d.ts.map +1 -1
- package/dist/vitals.js +115 -16
- package/package.json +17 -3
- package/dist/__tests__/client.test.d.ts +0 -2
- package/dist/__tests__/client.test.d.ts.map +0 -1
- package/dist/__tests__/client.test.js +0 -103
- package/dist/__tests__/errors.test.d.ts +0 -2
- package/dist/__tests__/errors.test.d.ts.map +0 -1
- package/dist/__tests__/errors.test.js +0 -80
- package/dist/__tests__/replay-privacy.test.d.ts +0 -2
- package/dist/__tests__/replay-privacy.test.d.ts.map +0 -1
- package/dist/__tests__/replay-privacy.test.js +0 -124
- package/dist/__tests__/replay-startup.test.d.ts +0 -2
- package/dist/__tests__/replay-startup.test.d.ts.map +0 -1
- package/dist/__tests__/replay-startup.test.js +0 -99
- package/dist/__tests__/replay.test.d.ts +0 -2
- package/dist/__tests__/replay.test.d.ts.map +0 -1
- package/dist/__tests__/replay.test.js +0 -217
- package/dist/__tests__/session.test.d.ts +0 -2
- package/dist/__tests__/session.test.d.ts.map +0 -1
- package/dist/__tests__/session.test.js +0 -46
- package/dist/__tests__/transport.test.d.ts +0 -2
- package/dist/__tests__/transport.test.d.ts.map +0 -1
- package/dist/__tests__/transport.test.js +0 -101
- package/dist/__tests__/types.test.d.ts +0 -2
- package/dist/__tests__/types.test.d.ts.map +0 -1
- 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
|
-
/**
|
|
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
|
-
/**
|
|
22
|
-
|
|
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
|
-
|
|
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
|
|
99
|
-
export type
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
export
|
|
103
|
-
[
|
|
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
|
-
|
|
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
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
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
|
|
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:
|
|
82
|
+
export declare function startVitalsCollection(transport: VitalsTransport, config: VitalsCollectorConfig, runtime?: VitalsRuntime): VitalsCollectorHandle;
|
|
30
83
|
//# sourceMappingURL=vitals.d.ts.map
|
package/dist/vitals.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitals.d.ts","sourceRoot":"","sources":["../src/vitals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;
|
|
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 (
|
|
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
|
-
|
|
36
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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.
|
|
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
|
-
"
|
|
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 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/client.test.ts"],"names":[],"mappings":""}
|