@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.
- package/dist/breadcrumbs.d.ts +10 -0
- package/dist/breadcrumbs.d.ts.map +1 -0
- package/dist/breadcrumbs.js +89 -0
- package/dist/breadcrumbs.js.map +1 -0
- package/dist/client.d.ts +40 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +348 -0
- package/dist/client.js.map +1 -0
- package/dist/fingerprint.d.ts +15 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +58 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/react.d.ts +39 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +53 -0
- package/dist/react.js.map +1 -0
- package/dist/server.d.ts +30 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +162 -0
- package/dist/server.js.map +1 -0
- package/dist/session.d.ts +13 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +93 -0
- package/dist/session.js.map +1 -0
- package/dist/transport.d.ts +31 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +91 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +60 -0
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React integration for @whaletools/telemetry.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* import { WhaleTelemetry, WhaleErrorBoundary } from '@whaletools/telemetry/react'
|
|
6
|
+
*
|
|
7
|
+
* <WhaleTelemetry apiKey="wk_live_..." storeId="...">
|
|
8
|
+
* <WhaleErrorBoundary fallback={<p>Something went wrong</p>}>
|
|
9
|
+
* <App />
|
|
10
|
+
* </WhaleErrorBoundary>
|
|
11
|
+
* </WhaleTelemetry>
|
|
12
|
+
*/
|
|
13
|
+
import { Component, type ReactNode } from "react";
|
|
14
|
+
import { type WhaleToolsConfig } from "./index.js";
|
|
15
|
+
interface WhaleTelemetryProps extends WhaleToolsConfig {
|
|
16
|
+
children: ReactNode;
|
|
17
|
+
}
|
|
18
|
+
export declare function WhaleTelemetry({ children, ...config }: WhaleTelemetryProps): ReactNode;
|
|
19
|
+
interface ErrorBoundaryProps {
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
fallback?: ReactNode | ((error: Error) => ReactNode);
|
|
22
|
+
onError?: (error: Error, errorInfo: {
|
|
23
|
+
componentStack: string;
|
|
24
|
+
}) => void;
|
|
25
|
+
}
|
|
26
|
+
interface ErrorBoundaryState {
|
|
27
|
+
error: Error | null;
|
|
28
|
+
}
|
|
29
|
+
export declare class WhaleErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
30
|
+
constructor(props: ErrorBoundaryProps);
|
|
31
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState;
|
|
32
|
+
componentDidCatch(error: Error, errorInfo: {
|
|
33
|
+
componentStack?: string;
|
|
34
|
+
}): void;
|
|
35
|
+
render(): ReactNode;
|
|
36
|
+
}
|
|
37
|
+
export { whaletools } from "./index.js";
|
|
38
|
+
export type { WhaleToolsConfig } from "./types.js";
|
|
39
|
+
//# sourceMappingURL=react.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,SAAS,EAAa,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7D,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAM/D,UAAU,mBAAoB,SAAQ,gBAAgB;IACpD,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,GAAG,MAAM,EACV,EAAE,mBAAmB,GAAG,SAAS,CAOjC;AAMD,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC;IACrD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACzE;AAED,UAAU,kBAAkB;IAC1B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED,qBAAa,kBAAmB,SAAQ,SAAS,CAC/C,kBAAkB,EAClB,kBAAkB,CACnB;gBACa,KAAK,EAAE,kBAAkB;IAKrC,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,kBAAkB;IAIjE,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAY7E,MAAM,IAAI,SAAS;CAUpB;AAGD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React integration for @whaletools/telemetry.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* import { WhaleTelemetry, WhaleErrorBoundary } from '@whaletools/telemetry/react'
|
|
6
|
+
*
|
|
7
|
+
* <WhaleTelemetry apiKey="wk_live_..." storeId="...">
|
|
8
|
+
* <WhaleErrorBoundary fallback={<p>Something went wrong</p>}>
|
|
9
|
+
* <App />
|
|
10
|
+
* </WhaleErrorBoundary>
|
|
11
|
+
* </WhaleTelemetry>
|
|
12
|
+
*/
|
|
13
|
+
import { Component, useEffect } from "react";
|
|
14
|
+
import { whaletools } from "./index.js";
|
|
15
|
+
export function WhaleTelemetry({ children, ...config }) {
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
whaletools.init(config);
|
|
18
|
+
return () => whaletools.destroy();
|
|
19
|
+
}, [config.apiKey, config.storeId]);
|
|
20
|
+
return children;
|
|
21
|
+
}
|
|
22
|
+
export class WhaleErrorBoundary extends Component {
|
|
23
|
+
constructor(props) {
|
|
24
|
+
super(props);
|
|
25
|
+
this.state = { error: null };
|
|
26
|
+
}
|
|
27
|
+
static getDerivedStateFromError(error) {
|
|
28
|
+
return { error };
|
|
29
|
+
}
|
|
30
|
+
componentDidCatch(error, errorInfo) {
|
|
31
|
+
// Report to WhaleTools
|
|
32
|
+
whaletools.captureError(error, {
|
|
33
|
+
componentStack: errorInfo.componentStack,
|
|
34
|
+
type: "react_error_boundary",
|
|
35
|
+
});
|
|
36
|
+
this.props.onError?.(error, {
|
|
37
|
+
componentStack: errorInfo.componentStack || "",
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
render() {
|
|
41
|
+
if (this.state.error) {
|
|
42
|
+
const { fallback } = this.props;
|
|
43
|
+
if (typeof fallback === "function") {
|
|
44
|
+
return fallback(this.state.error);
|
|
45
|
+
}
|
|
46
|
+
return fallback ?? null;
|
|
47
|
+
}
|
|
48
|
+
return this.props.children;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Re-export core for convenience
|
|
52
|
+
export { whaletools } from "./index.js";
|
|
53
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,SAAS,EAAE,SAAS,EAAkB,MAAM,OAAO,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAyB,MAAM,YAAY,CAAC;AAU/D,MAAM,UAAU,cAAc,CAAC,EAC7B,QAAQ,EACR,GAAG,MAAM,EACW;IACpB,SAAS,CAAC,GAAG,EAAE;QACb,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;IACpC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAEpC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAgBD,MAAM,OAAO,kBAAmB,SAAQ,SAGvC;IACC,YAAY,KAAyB;QACnC,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,IAAI,CAAC,KAAK,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC/B,CAAC;IAED,MAAM,CAAC,wBAAwB,CAAC,KAAY;QAC1C,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,iBAAiB,CAAC,KAAY,EAAE,SAAsC;QACpE,uBAAuB;QACvB,UAAU,CAAC,YAAY,CAAC,KAAK,EAAE;YAC7B,cAAc,EAAE,SAAS,CAAC,cAAc;YACxC,IAAI,EAAE,sBAAsB;SAC7B,CAAC,CAAC;QAEH,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE;YAC1B,cAAc,EAAE,SAAS,CAAC,cAAc,IAAI,EAAE;SAC/C,CAAC,CAAC;IACL,CAAC;IAED,MAAM;QACJ,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACrB,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;YAChC,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;gBACnC,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACpC,CAAC;YACD,OAAO,QAAQ,IAAI,IAAI,CAAC;QAC1B,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC7B,CAAC;CACF;AAED,iCAAiC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side helpers for @whaletools/telemetry.
|
|
3
|
+
* Works with Next.js API routes, Express, etc.
|
|
4
|
+
*
|
|
5
|
+
* Usage (Next.js App Router):
|
|
6
|
+
* import { withTelemetry } from '@whaletools/telemetry/server'
|
|
7
|
+
*
|
|
8
|
+
* export const GET = withTelemetry(async (req) => {
|
|
9
|
+
* return Response.json({ ok: true })
|
|
10
|
+
* }, { apiKey: 'wk_live_...', storeId: '...' })
|
|
11
|
+
*/
|
|
12
|
+
interface ServerConfig {
|
|
13
|
+
apiKey: string;
|
|
14
|
+
storeId: string;
|
|
15
|
+
endpoint?: string;
|
|
16
|
+
serviceName?: string;
|
|
17
|
+
environment?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Wrap a Next.js App Router handler with error telemetry.
|
|
21
|
+
* Catches unhandled errors, reports them, and re-throws.
|
|
22
|
+
*/
|
|
23
|
+
export declare function withTelemetry<T extends (...args: unknown[]) => Promise<Response>>(handler: T, config: ServerConfig): T;
|
|
24
|
+
/**
|
|
25
|
+
* Send a server-side error to WhaleTools telemetry.
|
|
26
|
+
* Use in catch blocks where withTelemetry isn't applicable.
|
|
27
|
+
*/
|
|
28
|
+
export declare function reportServerError(error: Error, config: ServerConfig, extra?: Record<string, unknown>): Promise<void>;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA0DD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,EAC/E,OAAO,EAAE,CAAC,EACV,MAAM,EAAE,YAAY,GACnB,CAAC,CAkEH;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,YAAY,EACpB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC,CAsDf"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side helpers for @whaletools/telemetry.
|
|
3
|
+
* Works with Next.js API routes, Express, etc.
|
|
4
|
+
*
|
|
5
|
+
* Usage (Next.js App Router):
|
|
6
|
+
* import { withTelemetry } from '@whaletools/telemetry/server'
|
|
7
|
+
*
|
|
8
|
+
* export const GET = withTelemetry(async (req) => {
|
|
9
|
+
* return Response.json({ ok: true })
|
|
10
|
+
* }, { apiKey: 'wk_live_...', storeId: '...' })
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_ENDPOINT = "https://whale-gateway.fly.dev";
|
|
13
|
+
// Simple server-side fingerprint (no SubtleCrypto needed in Node 18+)
|
|
14
|
+
async function serverFingerprint(type, message, source) {
|
|
15
|
+
const input = `${type}|${message}|${source}`;
|
|
16
|
+
if (typeof globalThis.crypto?.subtle?.digest === "function") {
|
|
17
|
+
const encoded = new TextEncoder().encode(input);
|
|
18
|
+
const hash = await crypto.subtle.digest("SHA-256", encoded);
|
|
19
|
+
return Array.from(new Uint8Array(hash))
|
|
20
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
21
|
+
.join("");
|
|
22
|
+
}
|
|
23
|
+
// Fallback
|
|
24
|
+
let hash = 5381;
|
|
25
|
+
for (let i = 0; i < input.length; i++) {
|
|
26
|
+
hash = ((hash << 5) + hash + input.charCodeAt(i)) >>> 0;
|
|
27
|
+
}
|
|
28
|
+
return hash.toString(16).padStart(16, "0");
|
|
29
|
+
}
|
|
30
|
+
function parseServerStack(stack) {
|
|
31
|
+
if (!stack)
|
|
32
|
+
return { file: "", line: 0, func: "" };
|
|
33
|
+
const lines = stack.split("\n");
|
|
34
|
+
for (const line of lines) {
|
|
35
|
+
const match = line.match(/at\s+(?:(.+?)\s+\()?(?:(.+?):(\d+):\d+)\)?/);
|
|
36
|
+
if (match) {
|
|
37
|
+
return {
|
|
38
|
+
func: match[1] || "<anonymous>",
|
|
39
|
+
file: match[2] || "",
|
|
40
|
+
line: parseInt(match[3] || "0", 10),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { file: "", line: 0, func: "" };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Wrap a Next.js App Router handler with error telemetry.
|
|
48
|
+
* Catches unhandled errors, reports them, and re-throws.
|
|
49
|
+
*/
|
|
50
|
+
export function withTelemetry(handler, config) {
|
|
51
|
+
const wrapped = async (...args) => {
|
|
52
|
+
const start = Date.now();
|
|
53
|
+
try {
|
|
54
|
+
return await handler(...args);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
58
|
+
const parsed = parseServerStack(err.stack || "");
|
|
59
|
+
const fp = await serverFingerprint(err.name, err.message, `${parsed.file}:${parsed.line}:${parsed.func}`);
|
|
60
|
+
const report = {
|
|
61
|
+
error_type: err.name || "Error",
|
|
62
|
+
error_message: err.message.slice(0, 1000),
|
|
63
|
+
stack_trace: (err.stack || "").slice(0, 8000),
|
|
64
|
+
fingerprint: fp,
|
|
65
|
+
severity: "error",
|
|
66
|
+
source_file: parsed.file,
|
|
67
|
+
source_line: parsed.line,
|
|
68
|
+
source_function: parsed.func,
|
|
69
|
+
tags: {
|
|
70
|
+
environment: config.environment || "production",
|
|
71
|
+
service: config.serviceName || "store_server",
|
|
72
|
+
},
|
|
73
|
+
extra: { handler: "api_route", duration_ms: Date.now() - start },
|
|
74
|
+
breadcrumbs: [],
|
|
75
|
+
occurred_at: new Date().toISOString(),
|
|
76
|
+
platform: "node",
|
|
77
|
+
};
|
|
78
|
+
// Fire-and-forget to gateway
|
|
79
|
+
const endpoint = config.endpoint || DEFAULT_ENDPOINT;
|
|
80
|
+
const url = `${endpoint}/v1/stores/${config.storeId}/telemetry/ingest`;
|
|
81
|
+
fetch(url, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: {
|
|
84
|
+
"Content-Type": "application/json",
|
|
85
|
+
"x-api-key": config.apiKey,
|
|
86
|
+
},
|
|
87
|
+
body: JSON.stringify({
|
|
88
|
+
session: {
|
|
89
|
+
session_id: "",
|
|
90
|
+
visitor_id: "server",
|
|
91
|
+
started_at: new Date(start).toISOString(),
|
|
92
|
+
page_url: "",
|
|
93
|
+
referrer: "",
|
|
94
|
+
user_agent: "",
|
|
95
|
+
screen_width: 0,
|
|
96
|
+
screen_height: 0,
|
|
97
|
+
device: "server",
|
|
98
|
+
language: "en",
|
|
99
|
+
},
|
|
100
|
+
errors: [report],
|
|
101
|
+
events: [],
|
|
102
|
+
vitals: [],
|
|
103
|
+
}),
|
|
104
|
+
}).catch(() => { });
|
|
105
|
+
throw error; // Re-throw so Next.js can handle the error
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
return wrapped;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Send a server-side error to WhaleTools telemetry.
|
|
112
|
+
* Use in catch blocks where withTelemetry isn't applicable.
|
|
113
|
+
*/
|
|
114
|
+
export async function reportServerError(error, config, extra) {
|
|
115
|
+
const parsed = parseServerStack(error.stack || "");
|
|
116
|
+
const fp = await serverFingerprint(error.name, error.message, `${parsed.file}:${parsed.line}:${parsed.func}`);
|
|
117
|
+
const report = {
|
|
118
|
+
error_type: error.name || "Error",
|
|
119
|
+
error_message: error.message.slice(0, 1000),
|
|
120
|
+
stack_trace: (error.stack || "").slice(0, 8000),
|
|
121
|
+
fingerprint: fp,
|
|
122
|
+
severity: "error",
|
|
123
|
+
source_file: parsed.file,
|
|
124
|
+
source_line: parsed.line,
|
|
125
|
+
source_function: parsed.func,
|
|
126
|
+
tags: {
|
|
127
|
+
environment: config.environment || "production",
|
|
128
|
+
service: config.serviceName || "store_server",
|
|
129
|
+
},
|
|
130
|
+
extra: extra || {},
|
|
131
|
+
breadcrumbs: [],
|
|
132
|
+
occurred_at: new Date().toISOString(),
|
|
133
|
+
platform: "node",
|
|
134
|
+
};
|
|
135
|
+
const endpoint = config.endpoint || DEFAULT_ENDPOINT;
|
|
136
|
+
const url = `${endpoint}/v1/stores/${config.storeId}/telemetry/ingest`;
|
|
137
|
+
await fetch(url, {
|
|
138
|
+
method: "POST",
|
|
139
|
+
headers: {
|
|
140
|
+
"Content-Type": "application/json",
|
|
141
|
+
"x-api-key": config.apiKey,
|
|
142
|
+
},
|
|
143
|
+
body: JSON.stringify({
|
|
144
|
+
session: {
|
|
145
|
+
session_id: "",
|
|
146
|
+
visitor_id: "server",
|
|
147
|
+
started_at: new Date().toISOString(),
|
|
148
|
+
page_url: "",
|
|
149
|
+
referrer: "",
|
|
150
|
+
user_agent: "",
|
|
151
|
+
screen_width: 0,
|
|
152
|
+
screen_height: 0,
|
|
153
|
+
device: "server",
|
|
154
|
+
language: "en",
|
|
155
|
+
},
|
|
156
|
+
errors: [report],
|
|
157
|
+
events: [],
|
|
158
|
+
vitals: [],
|
|
159
|
+
}),
|
|
160
|
+
}).catch(() => { });
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session and visitor ID management.
|
|
3
|
+
* Visitor ID persists across sessions (localStorage).
|
|
4
|
+
* Session ID resets after 30 min inactivity.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getVisitorId(): string;
|
|
7
|
+
export declare function getSessionId(): string;
|
|
8
|
+
export declare function getSessionStartedAt(): string;
|
|
9
|
+
/** Detect device type from user agent. */
|
|
10
|
+
export declare function detectDevice(): string;
|
|
11
|
+
/** Extract UTM params from URL. */
|
|
12
|
+
export declare function getUtmParams(): Record<string, string>;
|
|
13
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA0BH,wBAAgB,YAAY,IAAI,MAAM,CAWrC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAgBrC;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAO5C;AAED,0CAA0C;AAC1C,wBAAgB,YAAY,IAAI,MAAM,CAMrC;AAED,mCAAmC;AACnC,wBAAgB,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAarD"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session and visitor ID management.
|
|
3
|
+
* Visitor ID persists across sessions (localStorage).
|
|
4
|
+
* Session ID resets after 30 min inactivity.
|
|
5
|
+
*/
|
|
6
|
+
const VISITOR_KEY = "wt_vid";
|
|
7
|
+
const SESSION_KEY = "wt_sid";
|
|
8
|
+
const SESSION_TS_KEY = "wt_sts";
|
|
9
|
+
const SESSION_TIMEOUT = 30 * 60 * 1000; // 30 minutes
|
|
10
|
+
function generateId() {
|
|
11
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
12
|
+
return crypto.randomUUID();
|
|
13
|
+
}
|
|
14
|
+
// Fallback
|
|
15
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
16
|
+
const r = (Math.random() * 16) | 0;
|
|
17
|
+
return (c === "x" ? r : (r & 0x3) | 0x8).toString(16);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function getStorage() {
|
|
21
|
+
try {
|
|
22
|
+
return typeof localStorage !== "undefined" ? localStorage : null;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function getVisitorId() {
|
|
29
|
+
const storage = getStorage();
|
|
30
|
+
if (storage) {
|
|
31
|
+
let vid = storage.getItem(VISITOR_KEY);
|
|
32
|
+
if (!vid) {
|
|
33
|
+
vid = generateId();
|
|
34
|
+
storage.setItem(VISITOR_KEY, vid);
|
|
35
|
+
}
|
|
36
|
+
return vid;
|
|
37
|
+
}
|
|
38
|
+
return generateId();
|
|
39
|
+
}
|
|
40
|
+
export function getSessionId() {
|
|
41
|
+
const storage = getStorage();
|
|
42
|
+
if (!storage)
|
|
43
|
+
return generateId();
|
|
44
|
+
const now = Date.now();
|
|
45
|
+
const lastActivity = parseInt(storage.getItem(SESSION_TS_KEY) || "0", 10);
|
|
46
|
+
let sid = storage.getItem(SESSION_KEY);
|
|
47
|
+
// New session if expired or missing
|
|
48
|
+
if (!sid || now - lastActivity > SESSION_TIMEOUT) {
|
|
49
|
+
sid = generateId();
|
|
50
|
+
storage.setItem(SESSION_KEY, sid);
|
|
51
|
+
}
|
|
52
|
+
storage.setItem(SESSION_TS_KEY, String(now));
|
|
53
|
+
return sid;
|
|
54
|
+
}
|
|
55
|
+
export function getSessionStartedAt() {
|
|
56
|
+
const storage = getStorage();
|
|
57
|
+
if (storage) {
|
|
58
|
+
const ts = storage.getItem(SESSION_TS_KEY);
|
|
59
|
+
if (ts)
|
|
60
|
+
return new Date(parseInt(ts, 10)).toISOString();
|
|
61
|
+
}
|
|
62
|
+
return new Date().toISOString();
|
|
63
|
+
}
|
|
64
|
+
/** Detect device type from user agent. */
|
|
65
|
+
export function detectDevice() {
|
|
66
|
+
if (typeof navigator === "undefined")
|
|
67
|
+
return "server";
|
|
68
|
+
const ua = navigator.userAgent;
|
|
69
|
+
if (/Mobi|Android|iPhone|iPad/i.test(ua))
|
|
70
|
+
return "mobile";
|
|
71
|
+
if (/Tablet|iPad/i.test(ua))
|
|
72
|
+
return "tablet";
|
|
73
|
+
return "desktop";
|
|
74
|
+
}
|
|
75
|
+
/** Extract UTM params from URL. */
|
|
76
|
+
export function getUtmParams() {
|
|
77
|
+
if (typeof window === "undefined")
|
|
78
|
+
return {};
|
|
79
|
+
try {
|
|
80
|
+
const params = new URLSearchParams(window.location.search);
|
|
81
|
+
const utm = {};
|
|
82
|
+
for (const key of ["utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term"]) {
|
|
83
|
+
const val = params.get(key);
|
|
84
|
+
if (val)
|
|
85
|
+
utm[key] = val;
|
|
86
|
+
}
|
|
87
|
+
return utm;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return {};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batched HTTP transport to whale-gateway telemetry ingestion endpoint.
|
|
3
|
+
* Batches errors, events, and vitals — flushes on interval, threshold, or page unload.
|
|
4
|
+
*/
|
|
5
|
+
import type { ErrorPayload, AnalyticsEvent, WebVital, SessionContext, UserContext } from "./types.js";
|
|
6
|
+
export interface TransportConfig {
|
|
7
|
+
apiKey: string;
|
|
8
|
+
storeId: string;
|
|
9
|
+
endpoint: string;
|
|
10
|
+
flushInterval: number;
|
|
11
|
+
flushThreshold: number;
|
|
12
|
+
debug: boolean;
|
|
13
|
+
getSession: () => SessionContext;
|
|
14
|
+
getUser: () => UserContext | undefined;
|
|
15
|
+
}
|
|
16
|
+
export declare class Transport {
|
|
17
|
+
private errors;
|
|
18
|
+
private events;
|
|
19
|
+
private vitals;
|
|
20
|
+
private timer;
|
|
21
|
+
private config;
|
|
22
|
+
constructor(config: TransportConfig);
|
|
23
|
+
start(): void;
|
|
24
|
+
stop(): void;
|
|
25
|
+
queueError(error: ErrorPayload): void;
|
|
26
|
+
queueEvent(event: AnalyticsEvent): void;
|
|
27
|
+
queueVital(vital: WebVital): void;
|
|
28
|
+
private checkThreshold;
|
|
29
|
+
flush(useBeacon?: boolean): void;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,QAAQ,EAER,cAAc,EACd,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,cAAc,CAAC;IACjC,OAAO,EAAE,MAAM,WAAW,GAAG,SAAS,CAAC;CACxC;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,MAAM,CAAkB;gBAEpB,MAAM,EAAE,eAAe;IAInC,KAAK,IAAI,IAAI;IAab,IAAI,IAAI,IAAI;IAQZ,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAKrC,UAAU,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAKvC,UAAU,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAKjC,OAAO,CAAC,cAAc;IAOtB,KAAK,CAAC,SAAS,UAAQ,GAAG,IAAI;CAgD/B"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batched HTTP transport to whale-gateway telemetry ingestion endpoint.
|
|
3
|
+
* Batches errors, events, and vitals — flushes on interval, threshold, or page unload.
|
|
4
|
+
*/
|
|
5
|
+
export class Transport {
|
|
6
|
+
errors = [];
|
|
7
|
+
events = [];
|
|
8
|
+
vitals = [];
|
|
9
|
+
timer = null;
|
|
10
|
+
config;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
}
|
|
14
|
+
start() {
|
|
15
|
+
this.timer = setInterval(() => this.flush(), this.config.flushInterval);
|
|
16
|
+
// Flush on page unload
|
|
17
|
+
if (typeof window !== "undefined") {
|
|
18
|
+
const onUnload = () => this.flush(true);
|
|
19
|
+
window.addEventListener("visibilitychange", () => {
|
|
20
|
+
if (document.visibilityState === "hidden")
|
|
21
|
+
onUnload();
|
|
22
|
+
});
|
|
23
|
+
window.addEventListener("pagehide", onUnload);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
stop() {
|
|
27
|
+
if (this.timer) {
|
|
28
|
+
clearInterval(this.timer);
|
|
29
|
+
this.timer = null;
|
|
30
|
+
}
|
|
31
|
+
this.flush(true);
|
|
32
|
+
}
|
|
33
|
+
queueError(error) {
|
|
34
|
+
this.errors.push(error);
|
|
35
|
+
this.checkThreshold();
|
|
36
|
+
}
|
|
37
|
+
queueEvent(event) {
|
|
38
|
+
this.events.push(event);
|
|
39
|
+
this.checkThreshold();
|
|
40
|
+
}
|
|
41
|
+
queueVital(vital) {
|
|
42
|
+
this.vitals.push(vital);
|
|
43
|
+
// Don't auto-flush for vitals (they come in small numbers)
|
|
44
|
+
}
|
|
45
|
+
checkThreshold() {
|
|
46
|
+
const total = this.errors.length + this.events.length;
|
|
47
|
+
if (total >= this.config.flushThreshold) {
|
|
48
|
+
this.flush();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
flush(useBeacon = false) {
|
|
52
|
+
if (this.errors.length === 0 &&
|
|
53
|
+
this.events.length === 0 &&
|
|
54
|
+
this.vitals.length === 0) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const batch = {
|
|
58
|
+
session: this.config.getSession(),
|
|
59
|
+
user: this.config.getUser(),
|
|
60
|
+
errors: this.errors.splice(0),
|
|
61
|
+
events: this.events.splice(0),
|
|
62
|
+
vitals: this.vitals.splice(0),
|
|
63
|
+
};
|
|
64
|
+
const url = `${this.config.endpoint}/v1/stores/${this.config.storeId}/telemetry/ingest`;
|
|
65
|
+
const body = JSON.stringify(batch);
|
|
66
|
+
const headers = {
|
|
67
|
+
"Content-Type": "application/json",
|
|
68
|
+
"x-api-key": this.config.apiKey,
|
|
69
|
+
};
|
|
70
|
+
if (this.config.debug) {
|
|
71
|
+
console.log("[whaletools] flush", {
|
|
72
|
+
errors: batch.errors.length,
|
|
73
|
+
events: batch.events.length,
|
|
74
|
+
vitals: batch.vitals.length,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// Use sendBeacon on page unload (more reliable)
|
|
78
|
+
if (useBeacon && typeof navigator?.sendBeacon === "function") {
|
|
79
|
+
const blob = new Blob([body], { type: "application/json" });
|
|
80
|
+
navigator.sendBeacon(url + `?api_key=${this.config.apiKey}`, blob);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Normal fetch (fire-and-forget)
|
|
84
|
+
fetch(url, { method: "POST", headers, body, keepalive: true }).catch((err) => {
|
|
85
|
+
if (this.config.debug) {
|
|
86
|
+
console.error("[whaletools] flush failed:", err);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export interface WhaleToolsConfig {
|
|
2
|
+
/** API key (wk_live_... or wk_test_...) */
|
|
3
|
+
apiKey: string;
|
|
4
|
+
/** Store UUID */
|
|
5
|
+
storeId: string;
|
|
6
|
+
/** Gateway endpoint (defaults to https://whale-gateway.fly.dev) */
|
|
7
|
+
endpoint?: string;
|
|
8
|
+
/** Enable error tracking (default: true) */
|
|
9
|
+
errors?: boolean;
|
|
10
|
+
/** Enable analytics / page views (default: true) */
|
|
11
|
+
analytics?: boolean;
|
|
12
|
+
/** Enable Web Vitals collection (default: true) */
|
|
13
|
+
vitals?: boolean;
|
|
14
|
+
/** Enable breadcrumb collection (default: true) */
|
|
15
|
+
breadcrumbs?: boolean;
|
|
16
|
+
/** Environment tag (default: "production") */
|
|
17
|
+
environment?: string;
|
|
18
|
+
/** Service name tag (default: "store_client") */
|
|
19
|
+
serviceName?: string;
|
|
20
|
+
/** Service version tag */
|
|
21
|
+
serviceVersion?: string;
|
|
22
|
+
/** Flush interval in ms (default: 5000) */
|
|
23
|
+
flushInterval?: number;
|
|
24
|
+
/** Max items before auto-flush (default: 10) */
|
|
25
|
+
flushThreshold?: number;
|
|
26
|
+
/** Debug mode — logs to console (default: false) */
|
|
27
|
+
debug?: boolean;
|
|
28
|
+
/** Sample rate 0-1 for analytics events (default: 1) */
|
|
29
|
+
sampleRate?: number;
|
|
30
|
+
/** Callback before sending errors — return false to drop */
|
|
31
|
+
beforeSend?: (error: ErrorPayload) => ErrorPayload | false;
|
|
32
|
+
}
|
|
33
|
+
export interface ErrorPayload {
|
|
34
|
+
error_type: string;
|
|
35
|
+
error_message: string;
|
|
36
|
+
stack_trace: string;
|
|
37
|
+
fingerprint: string;
|
|
38
|
+
severity: "debug" | "info" | "warning" | "error" | "fatal";
|
|
39
|
+
source_file: string;
|
|
40
|
+
source_line: number;
|
|
41
|
+
source_function: string;
|
|
42
|
+
tags: Record<string, string>;
|
|
43
|
+
extra: Record<string, unknown>;
|
|
44
|
+
breadcrumbs: Breadcrumb[];
|
|
45
|
+
occurred_at: string;
|
|
46
|
+
platform: string;
|
|
47
|
+
}
|
|
48
|
+
export interface Breadcrumb {
|
|
49
|
+
timestamp: string;
|
|
50
|
+
category: string;
|
|
51
|
+
message: string;
|
|
52
|
+
level: "debug" | "info" | "warning" | "error";
|
|
53
|
+
data?: Record<string, unknown>;
|
|
54
|
+
}
|
|
55
|
+
export interface AnalyticsEvent {
|
|
56
|
+
event_name: string;
|
|
57
|
+
properties: Record<string, unknown>;
|
|
58
|
+
timestamp: string;
|
|
59
|
+
}
|
|
60
|
+
export interface WebVital {
|
|
61
|
+
name: "CLS" | "FID" | "LCP" | "INP" | "TTFB" | "FCP";
|
|
62
|
+
value: number;
|
|
63
|
+
rating: "good" | "needs-improvement" | "poor";
|
|
64
|
+
timestamp: string;
|
|
65
|
+
}
|
|
66
|
+
export interface TelemetryBatch {
|
|
67
|
+
session: SessionContext;
|
|
68
|
+
user?: UserContext;
|
|
69
|
+
errors: ErrorPayload[];
|
|
70
|
+
events: AnalyticsEvent[];
|
|
71
|
+
vitals: WebVital[];
|
|
72
|
+
}
|
|
73
|
+
export interface SessionContext {
|
|
74
|
+
session_id: string;
|
|
75
|
+
visitor_id: string;
|
|
76
|
+
started_at: string;
|
|
77
|
+
page_url: string;
|
|
78
|
+
referrer: string;
|
|
79
|
+
user_agent: string;
|
|
80
|
+
screen_width: number;
|
|
81
|
+
screen_height: number;
|
|
82
|
+
device: string;
|
|
83
|
+
language: string;
|
|
84
|
+
utm_source?: string;
|
|
85
|
+
utm_medium?: string;
|
|
86
|
+
utm_campaign?: string;
|
|
87
|
+
utm_content?: string;
|
|
88
|
+
utm_term?: string;
|
|
89
|
+
}
|
|
90
|
+
export interface UserContext {
|
|
91
|
+
user_id: string;
|
|
92
|
+
email?: string;
|
|
93
|
+
name?: string;
|
|
94
|
+
traits?: Record<string, unknown>;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=types.d.ts.map
|