@neowhale/telemetry 1.0.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.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Breadcrumb collector — records click, navigation, console, and network events.
3
+ * Maintains a ring buffer of the most recent 25 breadcrumbs.
4
+ */
5
+ import type { Breadcrumb } from "./types.js";
6
+ export declare function addBreadcrumb(category: string, message: string, level?: Breadcrumb["level"], data?: Record<string, unknown>): void;
7
+ export declare function getBreadcrumbs(): Breadcrumb[];
8
+ export declare function clearBreadcrumbs(): void;
9
+ export declare function installBreadcrumbs(): void;
10
+ //# sourceMappingURL=breadcrumbs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breadcrumbs.d.ts","sourceRoot":"","sources":["../src/breadcrumbs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAM7C,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,UAAU,CAAC,OAAO,CAAU,EACnC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAWN;AAED,wBAAgB,cAAc,IAAI,UAAU,EAAE,CAE7C;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAsEzC"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Breadcrumb collector — records click, navigation, console, and network events.
3
+ * Maintains a ring buffer of the most recent 25 breadcrumbs.
4
+ */
5
+ const MAX_BREADCRUMBS = 25;
6
+ const breadcrumbs = [];
7
+ let installed = false;
8
+ export function addBreadcrumb(category, message, level = "info", data) {
9
+ breadcrumbs.push({
10
+ timestamp: new Date().toISOString(),
11
+ category,
12
+ message,
13
+ level,
14
+ data,
15
+ });
16
+ if (breadcrumbs.length > MAX_BREADCRUMBS) {
17
+ breadcrumbs.shift();
18
+ }
19
+ }
20
+ export function getBreadcrumbs() {
21
+ return [...breadcrumbs];
22
+ }
23
+ export function clearBreadcrumbs() {
24
+ breadcrumbs.length = 0;
25
+ }
26
+ export function installBreadcrumbs() {
27
+ if (installed || typeof window === "undefined")
28
+ return;
29
+ installed = true;
30
+ // Click breadcrumbs
31
+ document.addEventListener("click", (e) => {
32
+ const target = e.target;
33
+ if (!target)
34
+ return;
35
+ const tag = target.tagName?.toLowerCase();
36
+ const text = target.textContent?.slice(0, 50) || "";
37
+ const id = target.id ? `#${target.id}` : "";
38
+ const cls = target.className
39
+ ? `.${String(target.className).split(" ")[0]}`
40
+ : "";
41
+ addBreadcrumb("ui.click", `${tag}${id}${cls} "${text.trim()}"`, "info");
42
+ }, { capture: true, passive: true });
43
+ // Navigation breadcrumbs (SPA route changes)
44
+ const origPushState = history.pushState;
45
+ history.pushState = function (...args) {
46
+ origPushState.apply(this, args);
47
+ addBreadcrumb("navigation", `${window.location.pathname}`, "info");
48
+ };
49
+ const origReplaceState = history.replaceState;
50
+ history.replaceState = function (...args) {
51
+ origReplaceState.apply(this, args);
52
+ addBreadcrumb("navigation", `${window.location.pathname}`, "info");
53
+ };
54
+ window.addEventListener("popstate", () => {
55
+ addBreadcrumb("navigation", `${window.location.pathname}`, "info");
56
+ });
57
+ // Console breadcrumbs (intercept console.warn/error)
58
+ const origWarn = console.warn;
59
+ console.warn = function (...args) {
60
+ addBreadcrumb("console", String(args[0]).slice(0, 200), "warning");
61
+ origWarn.apply(console, args);
62
+ };
63
+ const origError = console.error;
64
+ console.error = function (...args) {
65
+ addBreadcrumb("console", String(args[0]).slice(0, 200), "error");
66
+ origError.apply(console, args);
67
+ };
68
+ // Fetch breadcrumbs
69
+ const origFetch = window.fetch;
70
+ window.fetch = async function (input, init) {
71
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
72
+ const method = init?.method || "GET";
73
+ const start = Date.now();
74
+ try {
75
+ const response = await origFetch.call(window, input, init);
76
+ addBreadcrumb("http", `${method} ${url} [${response.status}]`, response.ok ? "info" : "warning", {
77
+ duration_ms: Date.now() - start,
78
+ });
79
+ return response;
80
+ }
81
+ catch (err) {
82
+ addBreadcrumb("http", `${method} ${url} [FAILED]`, "error", {
83
+ duration_ms: Date.now() - start,
84
+ });
85
+ throw err;
86
+ }
87
+ };
88
+ }
89
+ //# sourceMappingURL=breadcrumbs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breadcrumbs.js","sourceRoot":"","sources":["../src/breadcrumbs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Core WhaleTools telemetry client.
3
+ * Orchestrates error capture, analytics, Web Vitals, breadcrumbs, and transport.
4
+ */
5
+ import type { WhaleToolsConfig, ErrorPayload } from "./types.js";
6
+ export declare class WhaleToolsClient {
7
+ private config;
8
+ private transport;
9
+ private user;
10
+ private initialized;
11
+ private beforeSend?;
12
+ constructor();
13
+ init(config: WhaleToolsConfig): void;
14
+ /** Manually capture an error. */
15
+ captureError(error: Error | string, extra?: Record<string, unknown>): Promise<void>;
16
+ /** Capture a message as an error event. */
17
+ captureMessage(message: string, severity?: ErrorPayload["severity"], extra?: Record<string, unknown>): Promise<void>;
18
+ /** Track a custom analytics event. */
19
+ track(eventName: string, properties?: Record<string, unknown>): void;
20
+ /** Track a page view. */
21
+ page(properties?: Record<string, unknown>): void;
22
+ /** Identify the current user. */
23
+ identify(userId: string, traits?: Record<string, unknown>): void;
24
+ /** Add a manual breadcrumb. */
25
+ addBreadcrumb(category: string, message: string, data?: Record<string, unknown>): void;
26
+ /** Force flush all queued telemetry. */
27
+ flush(): void;
28
+ /** Shut down the client. */
29
+ destroy(): void;
30
+ private installErrorHandlers;
31
+ private installPageTracking;
32
+ private installWebVitals;
33
+ private observeVital;
34
+ private reportVital;
35
+ private rateVital;
36
+ private buildErrorPayload;
37
+ private getSessionContext;
38
+ private shouldSample;
39
+ }
40
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EAKb,MAAM,YAAY,CAAC;AAkBpB,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAaiB;IAC/B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,IAAI,CAA0B;IACtC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAC,CAAiC;;IA4BpD,IAAI,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI;IAoDpC,iCAAiC;IAC3B,YAAY,CAChB,KAAK,EAAE,KAAK,GAAG,MAAM,EACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC;IAOhB,2CAA2C;IACrC,cAAc,CAClB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,YAAY,CAAC,UAAU,CAAU,EAC3C,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC;IAOhB,sCAAsC;IACtC,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAiBpE,yBAAyB;IACzB,IAAI,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAWhD,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAWhE,+BAA+B;IAC/B,aAAa,CACX,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI;IAIP,wCAAwC;IACxC,KAAK,IAAI,IAAI;IAIb,4BAA4B;IAC5B,OAAO,IAAI,IAAI;IASf,OAAO,CAAC,oBAAoB;IAyC5B,OAAO,CAAC,mBAAmB;IAsB3B,OAAO,CAAC,gBAAgB;IAuCxB,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,SAAS;YAuBH,iBAAiB;IA+C/B,OAAO,CAAC,iBAAiB;IAyBzB,OAAO,CAAC,YAAY;CAGrB"}
package/dist/client.js ADDED
@@ -0,0 +1,348 @@
1
+ /**
2
+ * Core WhaleTools telemetry client.
3
+ * Orchestrates error capture, analytics, Web Vitals, breadcrumbs, and transport.
4
+ */
5
+ import { computeFingerprint, parseStack } from "./fingerprint.js";
6
+ import { getVisitorId, getSessionId, getSessionStartedAt, detectDevice, getUtmParams, } from "./session.js";
7
+ import { addBreadcrumb, getBreadcrumbs, installBreadcrumbs, } from "./breadcrumbs.js";
8
+ import { Transport } from "./transport.js";
9
+ const DEFAULT_ENDPOINT = "https://whale-gateway.fly.dev";
10
+ export class WhaleToolsClient {
11
+ config;
12
+ transport;
13
+ user;
14
+ initialized = false;
15
+ beforeSend;
16
+ constructor() {
17
+ // Defaults — overridden by init()
18
+ this.config = {
19
+ apiKey: "",
20
+ storeId: "",
21
+ endpoint: DEFAULT_ENDPOINT,
22
+ environment: "production",
23
+ serviceName: "store_client",
24
+ serviceVersion: "",
25
+ flushInterval: 5000,
26
+ flushThreshold: 10,
27
+ debug: false,
28
+ sampleRate: 1,
29
+ };
30
+ this.transport = new Transport({
31
+ apiKey: "",
32
+ storeId: "",
33
+ endpoint: DEFAULT_ENDPOINT,
34
+ flushInterval: 5000,
35
+ flushThreshold: 10,
36
+ debug: false,
37
+ getSession: () => this.getSessionContext(),
38
+ getUser: () => this.user,
39
+ });
40
+ }
41
+ init(config) {
42
+ if (this.initialized)
43
+ return;
44
+ this.initialized = true;
45
+ this.config = {
46
+ apiKey: config.apiKey,
47
+ storeId: config.storeId,
48
+ endpoint: config.endpoint || DEFAULT_ENDPOINT,
49
+ environment: config.environment || "production",
50
+ serviceName: config.serviceName || "store_client",
51
+ serviceVersion: config.serviceVersion || "",
52
+ flushInterval: config.flushInterval || 5000,
53
+ flushThreshold: config.flushThreshold || 10,
54
+ debug: config.debug || false,
55
+ sampleRate: config.sampleRate ?? 1,
56
+ };
57
+ this.beforeSend = config.beforeSend;
58
+ this.transport = new Transport({
59
+ apiKey: this.config.apiKey,
60
+ storeId: this.config.storeId,
61
+ endpoint: this.config.endpoint,
62
+ flushInterval: this.config.flushInterval,
63
+ flushThreshold: this.config.flushThreshold,
64
+ debug: this.config.debug,
65
+ getSession: () => this.getSessionContext(),
66
+ getUser: () => this.user,
67
+ });
68
+ // Install auto-capture
69
+ if (typeof window !== "undefined") {
70
+ if (config.breadcrumbs !== false)
71
+ installBreadcrumbs();
72
+ if (config.errors !== false)
73
+ this.installErrorHandlers();
74
+ if (config.analytics !== false)
75
+ this.installPageTracking();
76
+ if (config.vitals !== false)
77
+ this.installWebVitals();
78
+ }
79
+ this.transport.start();
80
+ if (this.config.debug) {
81
+ console.log("[whaletools] initialized", {
82
+ storeId: this.config.storeId,
83
+ endpoint: this.config.endpoint,
84
+ });
85
+ }
86
+ }
87
+ // ===========================================================================
88
+ // Public API
89
+ // ===========================================================================
90
+ /** Manually capture an error. */
91
+ async captureError(error, extra) {
92
+ const err = typeof error === "string" ? new Error(error) : error;
93
+ const payload = await this.buildErrorPayload(err, "error", extra);
94
+ if (!payload)
95
+ return;
96
+ this.transport.queueError(payload);
97
+ }
98
+ /** Capture a message as an error event. */
99
+ async captureMessage(message, severity = "info", extra) {
100
+ const err = new Error(message);
101
+ const payload = await this.buildErrorPayload(err, severity, extra);
102
+ if (!payload)
103
+ return;
104
+ this.transport.queueError(payload);
105
+ }
106
+ /** Track a custom analytics event. */
107
+ track(eventName, properties) {
108
+ if (!this.shouldSample())
109
+ return;
110
+ const event = {
111
+ event_name: eventName,
112
+ properties: {
113
+ ...properties,
114
+ page_url: typeof window !== "undefined" ? window.location.href : "",
115
+ page_title: typeof document !== "undefined" ? document.title : "",
116
+ },
117
+ timestamp: new Date().toISOString(),
118
+ };
119
+ this.transport.queueEvent(event);
120
+ addBreadcrumb("track", `${eventName}`, "info", properties);
121
+ }
122
+ /** Track a page view. */
123
+ page(properties) {
124
+ this.track("page_view", {
125
+ page_url: typeof window !== "undefined" ? window.location.href : "",
126
+ page_path: typeof window !== "undefined" ? window.location.pathname : "",
127
+ page_title: typeof document !== "undefined" ? document.title : "",
128
+ ...properties,
129
+ });
130
+ }
131
+ /** Identify the current user. */
132
+ identify(userId, traits) {
133
+ this.user = {
134
+ user_id: userId,
135
+ email: traits?.email,
136
+ name: traits?.name,
137
+ traits,
138
+ };
139
+ this.track("identify", { user_id: userId, ...traits });
140
+ }
141
+ /** Add a manual breadcrumb. */
142
+ addBreadcrumb(category, message, data) {
143
+ addBreadcrumb(category, message, "info", data);
144
+ }
145
+ /** Force flush all queued telemetry. */
146
+ flush() {
147
+ this.transport.flush();
148
+ }
149
+ /** Shut down the client. */
150
+ destroy() {
151
+ this.transport.stop();
152
+ this.initialized = false;
153
+ }
154
+ // ===========================================================================
155
+ // Internal: Error Handlers
156
+ // ===========================================================================
157
+ installErrorHandlers() {
158
+ // Global unhandled errors
159
+ window.addEventListener("error", (event) => {
160
+ if (event.error) {
161
+ this.buildErrorPayload(event.error, "error").then((p) => {
162
+ if (p)
163
+ this.transport.queueError(p);
164
+ });
165
+ }
166
+ else {
167
+ // Script errors without error object
168
+ this.buildErrorPayload(new Error(event.message || "Script error"), "error", {
169
+ filename: event.filename,
170
+ lineno: event.lineno,
171
+ colno: event.colno,
172
+ }).then((p) => {
173
+ if (p)
174
+ this.transport.queueError(p);
175
+ });
176
+ }
177
+ });
178
+ // Unhandled promise rejections
179
+ window.addEventListener("unhandledrejection", (event) => {
180
+ const error = event.reason instanceof Error
181
+ ? event.reason
182
+ : new Error(String(event.reason));
183
+ this.buildErrorPayload(error, "error", {
184
+ type: "unhandledrejection",
185
+ }).then((p) => {
186
+ if (p)
187
+ this.transport.queueError(p);
188
+ });
189
+ });
190
+ }
191
+ // ===========================================================================
192
+ // Internal: Page Tracking
193
+ // ===========================================================================
194
+ installPageTracking() {
195
+ // Track initial page load
196
+ this.page();
197
+ // Track SPA navigation via history
198
+ const origPush = history.pushState;
199
+ const self = this;
200
+ history.pushState = function (...args) {
201
+ origPush.apply(this, args);
202
+ // Defer to let React update document.title
203
+ setTimeout(() => self.page(), 50);
204
+ };
205
+ window.addEventListener("popstate", () => {
206
+ setTimeout(() => this.page(), 50);
207
+ });
208
+ }
209
+ // ===========================================================================
210
+ // Internal: Web Vitals
211
+ // ===========================================================================
212
+ installWebVitals() {
213
+ if (typeof PerformanceObserver === "undefined")
214
+ return;
215
+ // Largest Contentful Paint
216
+ this.observeVital("largest-contentful-paint", (entry) => {
217
+ this.reportVital("LCP", entry.startTime);
218
+ });
219
+ // First Input Delay / Interaction to Next Paint
220
+ this.observeVital("first-input", (entry) => {
221
+ const fid = entry.processingStart - entry.startTime;
222
+ this.reportVital("FID", fid);
223
+ });
224
+ // Cumulative Layout Shift
225
+ let clsValue = 0;
226
+ this.observeVital("layout-shift", (entry) => {
227
+ if (!entry.hadRecentInput) {
228
+ clsValue += entry.value;
229
+ }
230
+ });
231
+ // Report CLS on visibility change (when user leaves page)
232
+ if (typeof document !== "undefined") {
233
+ document.addEventListener("visibilitychange", () => {
234
+ if (document.visibilityState === "hidden" && clsValue > 0) {
235
+ this.reportVital("CLS", clsValue);
236
+ }
237
+ });
238
+ }
239
+ // First Contentful Paint
240
+ this.observeVital("paint", (entry) => {
241
+ if (entry.name === "first-contentful-paint") {
242
+ this.reportVital("FCP", entry.startTime);
243
+ }
244
+ });
245
+ }
246
+ observeVital(type, callback) {
247
+ try {
248
+ const observer = new PerformanceObserver((list) => {
249
+ for (const entry of list.getEntries()) {
250
+ callback(entry);
251
+ }
252
+ });
253
+ observer.observe({ type, buffered: true });
254
+ }
255
+ catch {
256
+ // Observer not supported for this type
257
+ }
258
+ }
259
+ reportVital(name, value) {
260
+ const rating = this.rateVital(name, value);
261
+ const vital = {
262
+ name,
263
+ value: Math.round(value * 1000) / 1000,
264
+ rating,
265
+ timestamp: new Date().toISOString(),
266
+ };
267
+ this.transport.queueVital(vital);
268
+ if (this.config.debug) {
269
+ console.log(`[whaletools] vital ${name}=${vital.value} (${rating})`);
270
+ }
271
+ }
272
+ rateVital(name, value) {
273
+ // Thresholds from web.dev
274
+ const thresholds = {
275
+ CLS: [0.1, 0.25],
276
+ FID: [100, 300],
277
+ LCP: [2500, 4000],
278
+ INP: [200, 500],
279
+ TTFB: [800, 1800],
280
+ FCP: [1800, 3000],
281
+ };
282
+ const [good, poor] = thresholds[name] || [1000, 3000];
283
+ if (value <= good)
284
+ return "good";
285
+ if (value <= poor)
286
+ return "needs-improvement";
287
+ return "poor";
288
+ }
289
+ // ===========================================================================
290
+ // Internal: Helpers
291
+ // ===========================================================================
292
+ async buildErrorPayload(error, severity, extra) {
293
+ const stack = error.stack || "";
294
+ const parsed = parseStack(stack);
295
+ const fingerprint = await computeFingerprint(error.name || "Error", error.message || "Unknown error", `${parsed.file}:${parsed.line}:${parsed.func}`);
296
+ let payload = {
297
+ error_type: error.name || "Error",
298
+ error_message: (error.message || "Unknown error").slice(0, 1000),
299
+ stack_trace: stack.slice(0, 8000),
300
+ fingerprint,
301
+ severity,
302
+ source_file: parsed.file,
303
+ source_line: parsed.line,
304
+ source_function: parsed.func,
305
+ tags: {
306
+ environment: this.config.environment,
307
+ service: this.config.serviceName,
308
+ },
309
+ extra: extra || {},
310
+ breadcrumbs: getBreadcrumbs(),
311
+ occurred_at: new Date().toISOString(),
312
+ platform: typeof window !== "undefined" ? "browser" : "node",
313
+ };
314
+ if (this.config.serviceVersion) {
315
+ payload.tags.version = this.config.serviceVersion;
316
+ }
317
+ // beforeSend hook
318
+ if (this.beforeSend) {
319
+ const result = this.beforeSend(payload);
320
+ if (result === false)
321
+ return null;
322
+ payload = result;
323
+ }
324
+ return payload;
325
+ }
326
+ getSessionContext() {
327
+ const utm = getUtmParams();
328
+ return {
329
+ session_id: getSessionId(),
330
+ visitor_id: getVisitorId(),
331
+ started_at: getSessionStartedAt(),
332
+ page_url: typeof window !== "undefined" ? window.location.href : "",
333
+ referrer: typeof document !== "undefined" ? document.referrer : "",
334
+ user_agent: typeof navigator !== "undefined" ? navigator.userAgent : "",
335
+ screen_width: typeof screen !== "undefined" ? screen.width : 0,
336
+ screen_height: typeof screen !== "undefined" ? screen.height : 0,
337
+ device: detectDevice(),
338
+ language: typeof navigator !== "undefined"
339
+ ? navigator.language
340
+ : "en",
341
+ ...utm,
342
+ };
343
+ }
344
+ shouldSample() {
345
+ return Math.random() < this.config.sampleRate;
346
+ }
347
+ }
348
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Error fingerprinting — SHA-256 hash of normalized (errorType | message | source).
3
+ * Same algorithm as supabase/functions/_shared/error-logger.ts
4
+ */
5
+ /** Normalize a message to group similar errors. */
6
+ export declare function normalizeMessage(msg: string): string;
7
+ /** Compute SHA-256 fingerprint for an error. */
8
+ export declare function computeFingerprint(errorType: string, errorMessage: string, sourceLocation: string): Promise<string>;
9
+ /** Parse a stack trace string into source location info. */
10
+ export declare function parseStack(stack: string): {
11
+ file: string;
12
+ line: number;
13
+ func: string;
14
+ };
15
+ //# sourceMappingURL=fingerprint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../src/fingerprint.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,mDAAmD;AACnD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAepD;AAED,gDAAgD;AAChD,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,CAkBjB;AAED,4DAA4D;AAC5D,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAkBA"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Error fingerprinting — SHA-256 hash of normalized (errorType | message | source).
3
+ * Same algorithm as supabase/functions/_shared/error-logger.ts
4
+ */
5
+ /** Normalize a message to group similar errors. */
6
+ export function normalizeMessage(msg) {
7
+ return msg
8
+ // UUIDs
9
+ .replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "<UUID>")
10
+ // URLs
11
+ .replace(/https?:\/\/[^\s)]+/g, "<URL>")
12
+ // Emails
13
+ .replace(/[\w.-]+@[\w.-]+\.\w+/g, "<EMAIL>")
14
+ // IPs
15
+ .replace(/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, "<IP>")
16
+ // Numbers (standalone)
17
+ .replace(/\b\d{3,}\b/g, "<N>")
18
+ // Quoted strings
19
+ .replace(/"[^"]{4,}"/g, '"<STR>"')
20
+ .replace(/'[^']{4,}'/g, "'<STR>'");
21
+ }
22
+ /** Compute SHA-256 fingerprint for an error. */
23
+ export async function computeFingerprint(errorType, errorMessage, sourceLocation) {
24
+ const normalized = normalizeMessage(errorMessage);
25
+ const input = `${errorType}|${normalized}|${sourceLocation}`;
26
+ // Use SubtleCrypto in browser, fall back to basic hash
27
+ if (typeof globalThis.crypto?.subtle?.digest === "function") {
28
+ const encoded = new TextEncoder().encode(input);
29
+ const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
30
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
31
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
32
+ }
33
+ // Fallback: simple djb2 hash (for environments without SubtleCrypto)
34
+ let hash = 5381;
35
+ for (let i = 0; i < input.length; i++) {
36
+ hash = ((hash << 5) + hash + input.charCodeAt(i)) >>> 0;
37
+ }
38
+ return hash.toString(16).padStart(16, "0");
39
+ }
40
+ /** Parse a stack trace string into source location info. */
41
+ export function parseStack(stack) {
42
+ if (!stack)
43
+ return { file: "", line: 0, func: "" };
44
+ const lines = stack.split("\n");
45
+ // Find first meaningful stack frame (skip Error: message line)
46
+ for (const line of lines) {
47
+ const match = line.match(/at\s+(?:(.+?)\s+\()?(?:(.+?):(\d+):\d+)\)?/);
48
+ if (match) {
49
+ return {
50
+ func: match[1] || "<anonymous>",
51
+ file: match[2] || "",
52
+ line: parseInt(match[3] || "0", 10),
53
+ };
54
+ }
55
+ }
56
+ return { file: "", line: 0, func: "" };
57
+ }
58
+ //# sourceMappingURL=fingerprint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fingerprint.js","sourceRoot":"","sources":["../src/fingerprint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @whaletools/telemetry — Error tracking, analytics & Web Vitals for WhaleTools stores.
3
+ *
4
+ * Usage:
5
+ * import { whaletools } from '@whaletools/telemetry'
6
+ *
7
+ * whaletools.init({ apiKey: 'wk_live_...', storeId: '...' })
8
+ * whaletools.track('purchase', { amount: 49.99 })
9
+ * whaletools.captureError(new Error('Something broke'))
10
+ */
11
+ import { WhaleToolsClient } from "./client.js";
12
+ export declare const whaletools: WhaleToolsClient;
13
+ export { WhaleToolsClient } from "./client.js";
14
+ export { addBreadcrumb } from "./breadcrumbs.js";
15
+ export type { WhaleToolsConfig, ErrorPayload, AnalyticsEvent, WebVital, Breadcrumb, TelemetryBatch, SessionContext, UserContext, } from "./types.js";
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,eAAO,MAAM,UAAU,kBAAyB,CAAC;AAGjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,UAAU,EACV,cAAc,EACd,cAAc,EACd,WAAW,GACZ,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @whaletools/telemetry — Error tracking, analytics & Web Vitals for WhaleTools stores.
3
+ *
4
+ * Usage:
5
+ * import { whaletools } from '@whaletools/telemetry'
6
+ *
7
+ * whaletools.init({ apiKey: 'wk_live_...', storeId: '...' })
8
+ * whaletools.track('purchase', { amount: 49.99 })
9
+ * whaletools.captureError(new Error('Something broke'))
10
+ */
11
+ import { WhaleToolsClient } from "./client.js";
12
+ // Singleton instance
13
+ export const whaletools = new WhaleToolsClient();
14
+ // Named exports for tree-shaking
15
+ export { WhaleToolsClient } from "./client.js";
16
+ export { addBreadcrumb } from "./breadcrumbs.js";
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,qBAAqB;AACrB,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,gBAAgB,EAAE,CAAC;AAEjD,iCAAiC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}