@spanwise/rum 0.4.3
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/client.d.ts +51 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +186 -0
- package/dist/client.js.map +1 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +67 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.d.ts +78 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +11 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/instrumentation/fetch.d.ts +16 -0
- package/dist/instrumentation/fetch.d.ts.map +1 -0
- package/dist/instrumentation/fetch.js +109 -0
- package/dist/instrumentation/fetch.js.map +1 -0
- package/dist/instrumentation/xhr.d.ts +16 -0
- package/dist/instrumentation/xhr.d.ts.map +1 -0
- package/dist/instrumentation/xhr.js +129 -0
- package/dist/instrumentation/xhr.js.map +1 -0
- package/dist/otel.d.ts +55 -0
- package/dist/otel.d.ts.map +1 -0
- package/dist/otel.js +91 -0
- package/dist/otel.js.map +1 -0
- package/dist/session.d.ts +11 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +176 -0
- package/dist/session.js.map +1 -0
- package/dist/span-transport.d.ts +18 -0
- package/dist/span-transport.d.ts.map +1 -0
- package/dist/span-transport.js +83 -0
- package/dist/span-transport.js.map +1 -0
- package/dist/spans.d.ts +30 -0
- package/dist/spans.d.ts.map +1 -0
- package/dist/spans.js +21 -0
- package/dist/spans.js.map +1 -0
- package/dist/trace-context.d.ts +37 -0
- package/dist/trace-context.d.ts.map +1 -0
- package/dist/trace-context.js +113 -0
- package/dist/trace-context.js.map +1 -0
- package/dist/transport.d.ts +15 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +101 -0
- package/dist/transport.js.map +1 -0
- package/dist/vitals.d.ts +12 -0
- package/dist/vitals.d.ts.map +1 -0
- package/dist/vitals.js +48 -0
- package/dist/vitals.js.map +1 -0
- package/package.json +38 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W3C Trace Context utilities for browser-side distributed tracing.
|
|
3
|
+
* Implements the W3C Trace Context specification:
|
|
4
|
+
* https://www.w3.org/TR/trace-context/
|
|
5
|
+
*
|
|
6
|
+
* traceparent format: version-traceId-spanId-flags
|
|
7
|
+
* Example: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
|
|
8
|
+
*/
|
|
9
|
+
export type TraceparentData = {
|
|
10
|
+
traceId: string;
|
|
11
|
+
spanId: string;
|
|
12
|
+
sampled: boolean;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Generate a random 32-character hex trace ID (16 bytes).
|
|
16
|
+
* Uses crypto.getRandomValues for cryptographically secure randomness.
|
|
17
|
+
*/
|
|
18
|
+
export declare function generateTraceId(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Generate a random 16-character hex span ID (8 bytes).
|
|
21
|
+
*/
|
|
22
|
+
export declare function generateSpanId(): string;
|
|
23
|
+
/**
|
|
24
|
+
* Parse a W3C traceparent header into its components.
|
|
25
|
+
* Returns null if the header is invalid.
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseTraceparent(header: string): TraceparentData | null;
|
|
28
|
+
/**
|
|
29
|
+
* Create a W3C traceparent header value.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createTraceparent(traceId: string, spanId: string, sampled: boolean): string;
|
|
32
|
+
/**
|
|
33
|
+
* Check if a URL should have trace context propagated.
|
|
34
|
+
* Propagates to same-origin, same root domain, or explicitly allowed origins.
|
|
35
|
+
*/
|
|
36
|
+
export declare function shouldPropagate(url: string, propagateToOrigins?: (string | RegExp)[]): boolean;
|
|
37
|
+
//# sourceMappingURL=trace-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-context.d.ts","sourceRoot":"","sources":["../src/trace-context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,eAAe,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;CAChB,CAAA;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAMxC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAMvC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAkBvE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,GACd,MAAM,CAGR;AAeD;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,GAAG,EAAE,MAAM,EACX,kBAAkB,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GACtC,OAAO,CAuCT"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W3C Trace Context utilities for browser-side distributed tracing.
|
|
3
|
+
* Implements the W3C Trace Context specification:
|
|
4
|
+
* https://www.w3.org/TR/trace-context/
|
|
5
|
+
*
|
|
6
|
+
* traceparent format: version-traceId-spanId-flags
|
|
7
|
+
* Example: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
|
|
8
|
+
*/
|
|
9
|
+
const TRACEPARENT_REGEX = /^00-([a-f0-9]{32})-([a-f0-9]{16})-([a-f0-9]{2})$/;
|
|
10
|
+
/**
|
|
11
|
+
* Generate a random 32-character hex trace ID (16 bytes).
|
|
12
|
+
* Uses crypto.getRandomValues for cryptographically secure randomness.
|
|
13
|
+
*/
|
|
14
|
+
export function generateTraceId() {
|
|
15
|
+
const bytes = new Uint8Array(16);
|
|
16
|
+
crypto.getRandomValues(bytes);
|
|
17
|
+
return Array.from(bytes)
|
|
18
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
19
|
+
.join("");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Generate a random 16-character hex span ID (8 bytes).
|
|
23
|
+
*/
|
|
24
|
+
export function generateSpanId() {
|
|
25
|
+
const bytes = new Uint8Array(8);
|
|
26
|
+
crypto.getRandomValues(bytes);
|
|
27
|
+
return Array.from(bytes)
|
|
28
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
29
|
+
.join("");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parse a W3C traceparent header into its components.
|
|
33
|
+
* Returns null if the header is invalid.
|
|
34
|
+
*/
|
|
35
|
+
export function parseTraceparent(header) {
|
|
36
|
+
const match = header.match(TRACEPARENT_REGEX);
|
|
37
|
+
if (!match)
|
|
38
|
+
return null;
|
|
39
|
+
const [, traceId, spanId, flags] = match;
|
|
40
|
+
if (!traceId || !spanId || !flags)
|
|
41
|
+
return null;
|
|
42
|
+
// Validate trace ID is not all zeros
|
|
43
|
+
if (traceId === "00000000000000000000000000000000")
|
|
44
|
+
return null;
|
|
45
|
+
// Validate span ID is not all zeros
|
|
46
|
+
if (spanId === "0000000000000000")
|
|
47
|
+
return null;
|
|
48
|
+
return {
|
|
49
|
+
traceId,
|
|
50
|
+
spanId,
|
|
51
|
+
sampled: (Number.parseInt(flags, 16) & 0x01) === 1,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Create a W3C traceparent header value.
|
|
56
|
+
*/
|
|
57
|
+
export function createTraceparent(traceId, spanId, sampled) {
|
|
58
|
+
const flags = sampled ? "01" : "00";
|
|
59
|
+
return `00-${traceId}-${spanId}-${flags}`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Extract the root domain from a hostname (e.g., "app.example.com" -> "example.com")
|
|
63
|
+
*/
|
|
64
|
+
function getRootDomain(hostname) {
|
|
65
|
+
const parts = hostname.split(".");
|
|
66
|
+
// Handle localhost and IP addresses
|
|
67
|
+
if (parts.length <= 2 || hostname === "localhost") {
|
|
68
|
+
return hostname;
|
|
69
|
+
}
|
|
70
|
+
// Return last two parts (e.g., "example.com")
|
|
71
|
+
return parts.slice(-2).join(".");
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check if a URL should have trace context propagated.
|
|
75
|
+
* Propagates to same-origin, same root domain, or explicitly allowed origins.
|
|
76
|
+
*/
|
|
77
|
+
export function shouldPropagate(url, propagateToOrigins) {
|
|
78
|
+
let parsedUrl;
|
|
79
|
+
try {
|
|
80
|
+
parsedUrl = new URL(url, window.location.origin);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
// Always propagate to same origin
|
|
86
|
+
if (parsedUrl.origin === window.location.origin) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
// Auto-propagate to same root domain (e.g., app.example.com -> api.example.com)
|
|
90
|
+
const currentRootDomain = getRootDomain(window.location.hostname);
|
|
91
|
+
const targetRootDomain = getRootDomain(parsedUrl.hostname);
|
|
92
|
+
if (currentRootDomain === targetRootDomain) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
// Check against allowlist
|
|
96
|
+
if (propagateToOrigins && propagateToOrigins.length > 0) {
|
|
97
|
+
for (const pattern of propagateToOrigins) {
|
|
98
|
+
if (typeof pattern === "string") {
|
|
99
|
+
if (parsedUrl.origin === pattern ||
|
|
100
|
+
parsedUrl.href.startsWith(pattern)) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else if (pattern instanceof RegExp) {
|
|
105
|
+
if (pattern.test(parsedUrl.href)) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=trace-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-context.js","sourceRoot":"","sources":["../src/trace-context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,iBAAiB,GAAG,kDAAkD,CAAA;AAQ5E;;;GAGG;AACH,MAAM,UAAU,eAAe;IAC9B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAA;IAChC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAA;IAC7B,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;SACtB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,CAAA;AACX,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC7B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAA;IAC/B,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAA;IAC7B,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;SACtB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,CAAA;AACX,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC9C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;IAC7C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IAEvB,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG,KAAK,CAAA;IACxC,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IAE9C,qCAAqC;IACrC,IAAI,OAAO,KAAK,kCAAkC;QAAE,OAAO,IAAI,CAAA;IAE/D,oCAAoC;IACpC,IAAI,MAAM,KAAK,kBAAkB;QAAE,OAAO,IAAI,CAAA;IAE9C,OAAO;QACN,OAAO;QACP,MAAM;QACN,OAAO,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;KAClD,CAAA;AACF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAChC,OAAe,EACf,MAAc,EACd,OAAgB;IAEhB,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;IACnC,OAAO,MAAM,OAAO,IAAI,MAAM,IAAI,KAAK,EAAE,CAAA;AAC1C,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,QAAgB;IACtC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACjC,oCAAoC;IACpC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;QACnD,OAAO,QAAQ,CAAA;IAChB,CAAC;IACD,8CAA8C;IAC9C,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACjC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC9B,GAAW,EACX,kBAAwC;IAExC,IAAI,SAAc,CAAA;IAClB,IAAI,CAAC;QACJ,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IACjD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAA;IACb,CAAC;IAED,kCAAkC;IAClC,IAAI,SAAS,CAAC,MAAM,KAAK,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QACjD,OAAO,IAAI,CAAA;IACZ,CAAC;IAED,gFAAgF;IAChF,MAAM,iBAAiB,GAAG,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IACjE,MAAM,gBAAgB,GAAG,aAAa,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;IAC1D,IAAI,iBAAiB,KAAK,gBAAgB,EAAE,CAAC;QAC5C,OAAO,IAAI,CAAA;IACZ,CAAC;IAED,0BAA0B;IAC1B,IAAI,kBAAkB,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzD,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;YAC1C,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;gBACjC,IACC,SAAS,CAAC,MAAM,KAAK,OAAO;oBAC5B,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EACjC,CAAC;oBACF,OAAO,IAAI,CAAA;gBACZ,CAAC;YACF,CAAC;iBAAM,IAAI,OAAO,YAAY,MAAM,EAAE,CAAC;gBACtC,IAAI,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;oBAClC,OAAO,IAAI,CAAA;gBACZ,CAAC;YACF,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,KAAK,CAAA;AACb,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RumEvent } from "./events.js";
|
|
2
|
+
export type TransportConfig = {
|
|
3
|
+
apiKey: string;
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
serviceName?: string;
|
|
6
|
+
flushInterval?: number;
|
|
7
|
+
maxBatchSize?: number;
|
|
8
|
+
};
|
|
9
|
+
export declare function createTransport(config: TransportConfig): {
|
|
10
|
+
enqueue: (event: RumEvent) => void;
|
|
11
|
+
flush: () => Promise<void>;
|
|
12
|
+
flushBeacon: () => void;
|
|
13
|
+
};
|
|
14
|
+
export type Transport = ReturnType<typeof createTransport>;
|
|
15
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE3C,MAAM,MAAM,eAAe,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAKD,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe;qBAgF9B,QAAQ,KAAG,IAAI;iBAlEf,OAAO,CAAC,IAAI,CAAC;uBAiCb,IAAI;EA8D5B;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAA"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const DEFAULT_FLUSH_INTERVAL = 5000;
|
|
2
|
+
const DEFAULT_MAX_BATCH_SIZE = 10;
|
|
3
|
+
export function createTransport(config) {
|
|
4
|
+
const queue = [];
|
|
5
|
+
let flushTimer = null;
|
|
6
|
+
const flushInterval = config.flushInterval ?? DEFAULT_FLUSH_INTERVAL;
|
|
7
|
+
const maxBatchSize = config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;
|
|
8
|
+
function scheduleFlush() {
|
|
9
|
+
if (flushTimer)
|
|
10
|
+
return;
|
|
11
|
+
flushTimer = setTimeout(() => {
|
|
12
|
+
flushTimer = null;
|
|
13
|
+
flush();
|
|
14
|
+
}, flushInterval);
|
|
15
|
+
}
|
|
16
|
+
async function flush() {
|
|
17
|
+
if (queue.length === 0)
|
|
18
|
+
return;
|
|
19
|
+
const batch = queue.splice(0, maxBatchSize);
|
|
20
|
+
try {
|
|
21
|
+
const response = await fetch(`${config.baseUrl}/v1/ingest/rum`, {
|
|
22
|
+
method: "POST",
|
|
23
|
+
headers: {
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
"x-api-key": config.apiKey,
|
|
26
|
+
},
|
|
27
|
+
body: JSON.stringify({
|
|
28
|
+
serviceName: config.serviceName,
|
|
29
|
+
events: batch,
|
|
30
|
+
}),
|
|
31
|
+
keepalive: true,
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
// Re-queue failed events (but limit retries)
|
|
35
|
+
console.warn("[Spanwise RUM] Failed to send events:", response.status);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
console.warn("[Spanwise RUM] Failed to send events:", error);
|
|
40
|
+
}
|
|
41
|
+
// Continue flushing if more events in queue
|
|
42
|
+
if (queue.length > 0) {
|
|
43
|
+
scheduleFlush();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function flushBeacon() {
|
|
47
|
+
if (queue.length === 0)
|
|
48
|
+
return;
|
|
49
|
+
const batch = queue.splice(0);
|
|
50
|
+
const body = JSON.stringify({
|
|
51
|
+
serviceName: config.serviceName,
|
|
52
|
+
events: batch,
|
|
53
|
+
});
|
|
54
|
+
// Use fetch with keepalive for better reliability during page unload
|
|
55
|
+
// Falls back to sendBeacon if fetch fails
|
|
56
|
+
try {
|
|
57
|
+
fetch(`${config.baseUrl}/v1/ingest/rum`, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: {
|
|
60
|
+
"Content-Type": "application/json",
|
|
61
|
+
"x-api-key": config.apiKey,
|
|
62
|
+
},
|
|
63
|
+
body,
|
|
64
|
+
keepalive: true,
|
|
65
|
+
}).catch(() => {
|
|
66
|
+
// Silently ignore - page is likely unloading
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Fallback to sendBeacon
|
|
71
|
+
const blob = new Blob([body], { type: "application/json" });
|
|
72
|
+
navigator.sendBeacon(`${config.baseUrl}/v1/ingest/rum?key=${config.apiKey}`, blob);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function enqueue(event) {
|
|
76
|
+
queue.push(event);
|
|
77
|
+
if (queue.length >= maxBatchSize) {
|
|
78
|
+
flush();
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
scheduleFlush();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function setupUnloadHandler() {
|
|
85
|
+
window.addEventListener("visibilitychange", () => {
|
|
86
|
+
if (document.visibilityState === "hidden") {
|
|
87
|
+
flushBeacon();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
window.addEventListener("pagehide", () => {
|
|
91
|
+
flushBeacon();
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
setupUnloadHandler();
|
|
95
|
+
return {
|
|
96
|
+
enqueue,
|
|
97
|
+
flush,
|
|
98
|
+
flushBeacon,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//# 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/vitals.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { VitalsEvent } from "./events.js";
|
|
2
|
+
export type VitalsData = {
|
|
3
|
+
lcp?: number;
|
|
4
|
+
fid?: number;
|
|
5
|
+
cls?: number;
|
|
6
|
+
inp?: number;
|
|
7
|
+
ttfb?: number;
|
|
8
|
+
fcp?: number;
|
|
9
|
+
};
|
|
10
|
+
export type VitalsCallback = (event: VitalsEvent) => void;
|
|
11
|
+
export declare function setupVitalsTracking(sessionId: string, userId: string | undefined, onVitals: VitalsCallback, onFlush?: () => void): void;
|
|
12
|
+
//# sourceMappingURL=vitals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitals.d.ts","sourceRoot":"","sources":["../src/vitals.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAG9C,MAAM,MAAM,UAAU,GAAG;IACxB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;AAEzD,wBAAgB,mBAAmB,CAClC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,QAAQ,EAAE,cAAc,EACxB,OAAO,CAAC,EAAE,MAAM,IAAI,GAClB,IAAI,CA8CN"}
|
package/dist/vitals.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { onCLS, onFCP, onFID, onINP, onLCP, onTTFB } from "web-vitals";
|
|
2
|
+
import { createBaseEvent } from "./events.js";
|
|
3
|
+
export function setupVitalsTracking(sessionId, userId, onVitals, onFlush) {
|
|
4
|
+
const vitalsData = {};
|
|
5
|
+
let sent = false;
|
|
6
|
+
// Send all accumulated vitals once
|
|
7
|
+
function sendVitals() {
|
|
8
|
+
if (sent)
|
|
9
|
+
return;
|
|
10
|
+
if (Object.keys(vitalsData).length === 0)
|
|
11
|
+
return;
|
|
12
|
+
sent = true;
|
|
13
|
+
const event = {
|
|
14
|
+
...createBaseEvent("vitals", sessionId, userId),
|
|
15
|
+
type: "vitals",
|
|
16
|
+
data: { ...vitalsData },
|
|
17
|
+
};
|
|
18
|
+
onVitals(event);
|
|
19
|
+
onFlush?.();
|
|
20
|
+
}
|
|
21
|
+
// Register web-vitals callbacks FIRST - they add internal visibilitychange
|
|
22
|
+
// listeners that will fire before ours and populate vitalsData
|
|
23
|
+
onLCP((metric) => {
|
|
24
|
+
vitalsData.lcp = metric.value;
|
|
25
|
+
});
|
|
26
|
+
onFID((metric) => {
|
|
27
|
+
vitalsData.fid = metric.value;
|
|
28
|
+
});
|
|
29
|
+
onCLS((metric) => {
|
|
30
|
+
vitalsData.cls = metric.value;
|
|
31
|
+
});
|
|
32
|
+
onINP((metric) => {
|
|
33
|
+
vitalsData.inp = metric.value;
|
|
34
|
+
});
|
|
35
|
+
onTTFB((metric) => {
|
|
36
|
+
vitalsData.ttfb = metric.value;
|
|
37
|
+
});
|
|
38
|
+
onFCP((metric) => {
|
|
39
|
+
vitalsData.fcp = metric.value;
|
|
40
|
+
});
|
|
41
|
+
// Register our listener AFTER web-vitals - fires after their callbacks populate data
|
|
42
|
+
document.addEventListener("visibilitychange", () => {
|
|
43
|
+
if (document.visibilityState === "hidden") {
|
|
44
|
+
sendVitals();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=vitals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitals.js","sourceRoot":"","sources":["../src/vitals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAEtE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAa7C,MAAM,UAAU,mBAAmB,CAClC,SAAiB,EACjB,MAA0B,EAC1B,QAAwB,EACxB,OAAoB;IAEpB,MAAM,UAAU,GAAe,EAAE,CAAA;IACjC,IAAI,IAAI,GAAG,KAAK,CAAA;IAEhB,mCAAmC;IACnC,SAAS,UAAU;QAClB,IAAI,IAAI;YAAE,OAAM;QAChB,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,OAAM;QAEhD,IAAI,GAAG,IAAI,CAAA;QACX,MAAM,KAAK,GAAgB;YAC1B,GAAG,eAAe,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC;YAC/C,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,EAAE,GAAG,UAAU,EAAE;SACvB,CAAA;QACD,QAAQ,CAAC,KAAK,CAAC,CAAA;QACf,OAAO,EAAE,EAAE,CAAA;IACZ,CAAC;IAED,2EAA2E;IAC3E,+DAA+D;IAC/D,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE;QAChB,UAAU,CAAC,GAAG,GAAG,MAAM,CAAC,KAAK,CAAA;IAC9B,CAAC,CAAC,CAAA;IACF,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE;QAChB,UAAU,CAAC,GAAG,GAAG,MAAM,CAAC,KAAK,CAAA;IAC9B,CAAC,CAAC,CAAA;IACF,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE;QAChB,UAAU,CAAC,GAAG,GAAG,MAAM,CAAC,KAAK,CAAA;IAC9B,CAAC,CAAC,CAAA;IACF,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE;QAChB,UAAU,CAAC,GAAG,GAAG,MAAM,CAAC,KAAK,CAAA;IAC9B,CAAC,CAAC,CAAA;IACF,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE;QACjB,UAAU,CAAC,IAAI,GAAG,MAAM,CAAC,KAAK,CAAA;IAC/B,CAAC,CAAC,CAAA;IACF,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE;QAChB,UAAU,CAAC,GAAG,GAAG,MAAM,CAAC,KAAK,CAAA;IAC9B,CAAC,CAAC,CAAA;IAEF,qFAAqF;IACrF,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAClD,IAAI,QAAQ,CAAC,eAAe,KAAK,QAAQ,EAAE,CAAC;YAC3C,UAAU,EAAE,CAAA;QACb,CAAC;IACF,CAAC,CAAC,CAAA;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@spanwise/rum",
|
|
3
|
+
"version": "0.4.3",
|
|
4
|
+
"description": "Real User Monitoring SDK for Spanwise - track sessions, page views, clicks, and Core Web Vitals",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": ["dist"],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"dev": "tsc --watch",
|
|
21
|
+
"clean": "rm -rf dist"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"spanwise",
|
|
25
|
+
"rum",
|
|
26
|
+
"monitoring",
|
|
27
|
+
"apm",
|
|
28
|
+
"web-vitals",
|
|
29
|
+
"analytics"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"web-vitals": "^4.2.4"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"typescript": "^5.7.2"
|
|
37
|
+
}
|
|
38
|
+
}
|