@traffical/node 0.1.2
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 +154 -0
- package/dist/client.d.ts +200 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +419 -0
- package/dist/client.js.map +1 -0
- package/dist/event-batcher.d.ts +74 -0
- package/dist/event-batcher.d.ts.map +1 -0
- package/dist/event-batcher.js +165 -0
- package/dist/event-batcher.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
- package/src/client.ts +564 -0
- package/src/event-batcher.ts +210 -0
- package/src/index.ts +17 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EventBatcher - Batched event transport for Node.js environments.
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Batches events for efficient network usage
|
|
6
|
+
* - Flushes on batch size or interval (whichever comes first)
|
|
7
|
+
* - Graceful shutdown with final flush
|
|
8
|
+
* - Error handling with configurable callback
|
|
9
|
+
*
|
|
10
|
+
* Unlike the browser EventLogger, this implementation:
|
|
11
|
+
* - Does not use sendBeacon (Node.js doesn't have it)
|
|
12
|
+
* - Does not persist failed events to storage
|
|
13
|
+
* - Uses standard fetch for HTTP requests
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { TrackableEvent } from "@traffical/core";
|
|
17
|
+
|
|
18
|
+
const DEFAULT_BATCH_SIZE = 10;
|
|
19
|
+
const DEFAULT_FLUSH_INTERVAL_MS = 30_000; // 30 seconds
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Options for EventBatcher.
|
|
23
|
+
*/
|
|
24
|
+
export interface EventBatcherOptions {
|
|
25
|
+
/** API endpoint for events */
|
|
26
|
+
endpoint: string;
|
|
27
|
+
/** API key for authentication */
|
|
28
|
+
apiKey: string;
|
|
29
|
+
/** Max events before auto-flush (default: 10) */
|
|
30
|
+
batchSize?: number;
|
|
31
|
+
/** Auto-flush interval in ms (default: 30000) */
|
|
32
|
+
flushIntervalMs?: number;
|
|
33
|
+
/** Callback on flush error */
|
|
34
|
+
onError?: (error: Error) => void;
|
|
35
|
+
/** Enable debug logging */
|
|
36
|
+
debug?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class EventBatcher {
|
|
40
|
+
private readonly _endpoint: string;
|
|
41
|
+
private readonly _apiKey: string;
|
|
42
|
+
private readonly _batchSize: number;
|
|
43
|
+
private readonly _flushIntervalMs: number;
|
|
44
|
+
private readonly _onError?: (error: Error) => void;
|
|
45
|
+
private readonly _debug: boolean;
|
|
46
|
+
|
|
47
|
+
private _queue: TrackableEvent[] = [];
|
|
48
|
+
private _flushTimer: ReturnType<typeof setInterval> | null = null;
|
|
49
|
+
private _isFlushing = false;
|
|
50
|
+
private _isDestroyed = false;
|
|
51
|
+
|
|
52
|
+
constructor(options: EventBatcherOptions) {
|
|
53
|
+
this._endpoint = options.endpoint;
|
|
54
|
+
this._apiKey = options.apiKey;
|
|
55
|
+
this._batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
56
|
+
this._flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
|
|
57
|
+
this._onError = options.onError;
|
|
58
|
+
this._debug = options.debug ?? false;
|
|
59
|
+
|
|
60
|
+
// Start flush timer
|
|
61
|
+
this._startFlushTimer();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Log an event (added to batch queue).
|
|
66
|
+
*/
|
|
67
|
+
log(event: TrackableEvent): void {
|
|
68
|
+
if (this._isDestroyed) {
|
|
69
|
+
this._log("Attempted to log event after destroy, ignoring");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
this._queue.push(event);
|
|
74
|
+
this._log(`Event queued (queue size: ${this._queue.length})`);
|
|
75
|
+
|
|
76
|
+
// Auto-flush if batch is full
|
|
77
|
+
if (this._queue.length >= this._batchSize) {
|
|
78
|
+
this._log("Batch size reached, flushing");
|
|
79
|
+
this.flush().catch(() => {
|
|
80
|
+
// Errors handled in flush
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Flush all queued events immediately.
|
|
87
|
+
*/
|
|
88
|
+
async flush(): Promise<void> {
|
|
89
|
+
if (this._isFlushing || this._queue.length === 0) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this._isFlushing = true;
|
|
94
|
+
|
|
95
|
+
// Take current queue
|
|
96
|
+
const events = [...this._queue];
|
|
97
|
+
this._queue = [];
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
await this._sendEvents(events);
|
|
101
|
+
this._log(`Flushed ${events.length} events successfully`);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
// Put events back in queue for retry (at the front)
|
|
104
|
+
this._queue.unshift(...events);
|
|
105
|
+
this._log(`Flush failed, ${events.length} events re-queued`);
|
|
106
|
+
this._onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
107
|
+
} finally {
|
|
108
|
+
this._isFlushing = false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get the number of events in the queue.
|
|
114
|
+
*/
|
|
115
|
+
get queueSize(): number {
|
|
116
|
+
return this._queue.length;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Check if the batcher is destroyed.
|
|
121
|
+
*/
|
|
122
|
+
get isDestroyed(): boolean {
|
|
123
|
+
return this._isDestroyed;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Destroy the batcher (cleanup timers and flush remaining events).
|
|
128
|
+
*/
|
|
129
|
+
async destroy(): Promise<void> {
|
|
130
|
+
if (this._isDestroyed) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this._isDestroyed = true;
|
|
135
|
+
|
|
136
|
+
// Stop timer
|
|
137
|
+
if (this._flushTimer) {
|
|
138
|
+
clearInterval(this._flushTimer);
|
|
139
|
+
this._flushTimer = null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Final flush
|
|
143
|
+
if (this._queue.length > 0) {
|
|
144
|
+
this._log(`Destroying with ${this._queue.length} events in queue, flushing`);
|
|
145
|
+
await this.flush();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Synchronous destroy (for process exit handlers).
|
|
151
|
+
* Does not wait for flush to complete.
|
|
152
|
+
*/
|
|
153
|
+
destroySync(): void {
|
|
154
|
+
this._isDestroyed = true;
|
|
155
|
+
|
|
156
|
+
if (this._flushTimer) {
|
|
157
|
+
clearInterval(this._flushTimer);
|
|
158
|
+
this._flushTimer = null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Attempt to flush but don't wait
|
|
162
|
+
if (this._queue.length > 0) {
|
|
163
|
+
this.flush().catch(() => {
|
|
164
|
+
// Best effort on shutdown
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private async _sendEvents(events: TrackableEvent[]): Promise<void> {
|
|
170
|
+
const response = await fetch(this._endpoint, {
|
|
171
|
+
method: "POST",
|
|
172
|
+
headers: {
|
|
173
|
+
"Content-Type": "application/json",
|
|
174
|
+
Authorization: `Bearer ${this._apiKey}`,
|
|
175
|
+
},
|
|
176
|
+
body: JSON.stringify({ events }),
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
if (!response.ok) {
|
|
180
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private _startFlushTimer(): void {
|
|
185
|
+
if (this._flushIntervalMs <= 0) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
this._flushTimer = setInterval(() => {
|
|
190
|
+
if (this._queue.length > 0) {
|
|
191
|
+
this.flush().catch(() => {
|
|
192
|
+
// Errors handled in flush
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}, this._flushIntervalMs);
|
|
196
|
+
|
|
197
|
+
// Unref the timer so it doesn't keep the process alive
|
|
198
|
+
// This is important for Node.js servers that need to shutdown gracefully
|
|
199
|
+
if (typeof this._flushTimer.unref === "function") {
|
|
200
|
+
this._flushTimer.unref();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private _log(message: string): void {
|
|
205
|
+
if (this._debug) {
|
|
206
|
+
console.log(`[Traffical EventBatcher] ${message}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @traffical/node
|
|
3
|
+
*
|
|
4
|
+
* Traffical SDK for Node.js environments.
|
|
5
|
+
* Provides HTTP client with caching, background refresh, and event tracking.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Re-export everything from core
|
|
9
|
+
export * from "@traffical/core";
|
|
10
|
+
|
|
11
|
+
// Export Node-specific client
|
|
12
|
+
export {
|
|
13
|
+
TrafficalClient,
|
|
14
|
+
createTrafficalClient,
|
|
15
|
+
createTrafficalClientSync,
|
|
16
|
+
} from "./client.js";
|
|
17
|
+
|