@nexussdk/tracker 0.0.1 → 0.0.4
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/README.md +170 -0
- package/dist/dev-server/cli.mjs +1027 -0
- package/dist/index.cjs +2 -3
- package/dist/index.d.mts +227 -20
- package/dist/index.d.ts +227 -20
- package/dist/index.global.js +3 -4
- package/dist/index.mjs +2 -3
- package/package.json +57 -5
- package/.turbo/turbo-build.log +0 -26
- package/dist/index.cjs.map +0 -1
- package/dist/index.global.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/src/breadcrumbs.ts +0 -156
- package/src/client.ts +0 -289
- package/src/fingerprint.ts +0 -107
- package/src/index.ts +0 -16
- package/src/listeners.ts +0 -49
- package/src/sanitizer.ts +0 -107
- package/src/transport.ts +0 -137
- package/tsconfig.json +0 -8
- package/tsup.config.ts +0 -19
package/src/transport.ts
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Hybrid transport dispatcher for error payload delivery.
|
|
3
|
-
* Prefers fetch with keepalive; falls back to navigator.sendBeacon during page unload.
|
|
4
|
-
* @module @nexus/sdk-tracker/transport
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { ErrorEventPayload } from '@nexussdk/contracts';
|
|
8
|
-
import { safeStringify, computeBackoffMs } from '@nexussdk/core';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Options for configuring the transport dispatcher.
|
|
12
|
-
*/
|
|
13
|
-
export interface TransportOptions {
|
|
14
|
-
/** Go-Gin ingestion endpoint URL. */
|
|
15
|
-
endpoint: string;
|
|
16
|
-
/** Public API key for Authorization header. */
|
|
17
|
-
apiKey: string;
|
|
18
|
-
/** Maximum retry attempts on network failures. Defaults to 2. */
|
|
19
|
-
maxRetries?: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Hybrid transport dispatcher that intelligently selects the delivery mechanism:
|
|
24
|
-
* - **Normal execution**: `fetch(url, { keepalive: true })` with retry
|
|
25
|
-
* - **Page teardown** (`visibilityState === 'hidden'` or `pagehide`): `navigator.sendBeacon`
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* const transport = new Transport({
|
|
29
|
-
* endpoint: 'http://localhost:8080/api/v1/telemetry/errors',
|
|
30
|
-
* apiKey: 'pk_live_...',
|
|
31
|
-
* });
|
|
32
|
-
* transport.send(errorPayload);
|
|
33
|
-
*/
|
|
34
|
-
export class Transport {
|
|
35
|
-
private readonly options: TransportOptions;
|
|
36
|
-
private readonly maxRetries: number;
|
|
37
|
-
private isPageHiding = false;
|
|
38
|
-
|
|
39
|
-
constructor(options: TransportOptions) {
|
|
40
|
-
this.options = options;
|
|
41
|
-
this.maxRetries = options.maxRetries ?? 2;
|
|
42
|
-
|
|
43
|
-
// Detect page teardown events to switch to sendBeacon
|
|
44
|
-
if (typeof document !== 'undefined') {
|
|
45
|
-
document.addEventListener('visibilitychange', () => {
|
|
46
|
-
if (document.visibilityState === 'hidden') {
|
|
47
|
-
this.isPageHiding = true;
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
if (typeof window !== 'undefined') {
|
|
52
|
-
window.addEventListener('pagehide', () => {
|
|
53
|
-
this.isPageHiding = true;
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Dispatches an error payload to the Go-Gin ingestion endpoint.
|
|
60
|
-
* Automatically selects fetch or sendBeacon based on page lifecycle state.
|
|
61
|
-
*
|
|
62
|
-
* @param payload - The sanitized {@link ErrorEventPayload} to transmit.
|
|
63
|
-
*
|
|
64
|
-
* @example
|
|
65
|
-
* transport.send({
|
|
66
|
-
* fingerprint: 'fp_abc123',
|
|
67
|
-
* errorType: 'TypeError',
|
|
68
|
-
* errorMessage: "Cannot read property 'map' of undefined",
|
|
69
|
-
* // ...
|
|
70
|
-
* });
|
|
71
|
-
*/
|
|
72
|
-
send(payload: ErrorEventPayload): void {
|
|
73
|
-
const body = safeStringify(payload);
|
|
74
|
-
|
|
75
|
-
// Use sendBeacon during page teardown — prevents cancelled fetch requests
|
|
76
|
-
if (
|
|
77
|
-
this.isPageHiding &&
|
|
78
|
-
typeof navigator !== 'undefined' &&
|
|
79
|
-
typeof navigator.sendBeacon === 'function'
|
|
80
|
-
) {
|
|
81
|
-
const blob = new Blob([body], { type: 'application/json' });
|
|
82
|
-
navigator.sendBeacon(this.options.endpoint, blob);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Normal execution: fetch with keepalive and exponential backoff retry
|
|
87
|
-
void this.sendWithRetry(body, 0);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Forces immediate flush of all pending events using sendBeacon.
|
|
92
|
-
* Called during manual flush or SDK destroy lifecycle.
|
|
93
|
-
*
|
|
94
|
-
* @param payload - The error payload to flush.
|
|
95
|
-
* @returns Promise that resolves when the beacon is dispatched.
|
|
96
|
-
*/
|
|
97
|
-
async flush(payload: ErrorEventPayload): Promise<void> {
|
|
98
|
-
const body = safeStringify(payload);
|
|
99
|
-
if (typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {
|
|
100
|
-
const blob = new Blob([body], { type: 'application/json' });
|
|
101
|
-
navigator.sendBeacon(this.options.endpoint, blob);
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
await this.sendWithRetry(body, 0);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
private async sendWithRetry(body: string, attempt: number): Promise<void> {
|
|
108
|
-
try {
|
|
109
|
-
const response = await fetch(this.options.endpoint, {
|
|
110
|
-
method: 'POST',
|
|
111
|
-
headers: {
|
|
112
|
-
'Content-Type': 'application/json',
|
|
113
|
-
Authorization: `Bearer ${this.options.apiKey}`,
|
|
114
|
-
},
|
|
115
|
-
body,
|
|
116
|
-
keepalive: true,
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
if (response.ok) return;
|
|
120
|
-
|
|
121
|
-
// Retry on 5xx
|
|
122
|
-
if (response.status >= 500 && attempt < this.maxRetries) {
|
|
123
|
-
const delay = computeBackoffMs(attempt, 1000, 30_000);
|
|
124
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
125
|
-
await this.sendWithRetry(body, attempt + 1);
|
|
126
|
-
}
|
|
127
|
-
} catch {
|
|
128
|
-
// Network failure — retry with backoff
|
|
129
|
-
if (attempt < this.maxRetries) {
|
|
130
|
-
const delay = computeBackoffMs(attempt, 1000, 30_000);
|
|
131
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
132
|
-
await this.sendWithRetry(body, attempt + 1);
|
|
133
|
-
}
|
|
134
|
-
// All retries exhausted — silently drop to prevent host app instability
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
package/tsconfig.json
DELETED
package/tsup.config.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'tsup';
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
entry: ['src/index.ts'],
|
|
5
|
-
format: ['esm', 'cjs', 'iife'],
|
|
6
|
-
globalName: 'NexusTracker',
|
|
7
|
-
dts: true,
|
|
8
|
-
splitting: false,
|
|
9
|
-
sourcemap: true,
|
|
10
|
-
clean: true,
|
|
11
|
-
minify: true,
|
|
12
|
-
treeshake: true,
|
|
13
|
-
target: 'es2022',
|
|
14
|
-
outExtension({ format }) {
|
|
15
|
-
return {
|
|
16
|
-
js: format === 'esm' ? '.mjs' : format === 'cjs' ? '.cjs' : '.global.js',
|
|
17
|
-
};
|
|
18
|
-
},
|
|
19
|
-
});
|