@bentolabs/sdk 1.0.0 → 1.0.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/index.d.ts +37 -0
- package/dist/index.js +216 -3
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -2,16 +2,29 @@ interface SDKConfig {
|
|
|
2
2
|
apiKey: string;
|
|
3
3
|
endpoint: string;
|
|
4
4
|
debug: boolean;
|
|
5
|
+
batchSize: number;
|
|
6
|
+
batchInterval: number;
|
|
7
|
+
enableRecording: boolean;
|
|
8
|
+
maxRetries: number;
|
|
9
|
+
baseRetryDelay: number;
|
|
5
10
|
}
|
|
6
11
|
interface SDKOptions {
|
|
7
12
|
endpoint?: string;
|
|
8
13
|
debug?: boolean;
|
|
14
|
+
batchSize?: number;
|
|
15
|
+
batchInterval?: number;
|
|
16
|
+
enableRecording?: boolean;
|
|
17
|
+
maxRetries?: number;
|
|
18
|
+
baseRetryDelay?: number;
|
|
9
19
|
}
|
|
10
20
|
export declare class BentoLabsSDK {
|
|
11
21
|
private config;
|
|
12
22
|
private sessionId;
|
|
13
23
|
private events;
|
|
14
24
|
private isRecording;
|
|
25
|
+
private batchTimer;
|
|
26
|
+
private retryTimer;
|
|
27
|
+
private stopRecording;
|
|
15
28
|
constructor();
|
|
16
29
|
/**
|
|
17
30
|
* Initialize the BentoLabs SDK
|
|
@@ -35,6 +48,22 @@ export declare class BentoLabsSDK {
|
|
|
35
48
|
* Start batching events for transmission
|
|
36
49
|
*/
|
|
37
50
|
private startBatching;
|
|
51
|
+
/**
|
|
52
|
+
* Add an event to the events array
|
|
53
|
+
*/
|
|
54
|
+
private addEvent;
|
|
55
|
+
/**
|
|
56
|
+
* Send batched events to the API with exponential backoff retry
|
|
57
|
+
*/
|
|
58
|
+
private sendBatch;
|
|
59
|
+
/**
|
|
60
|
+
* Schedule retry attempts for failed events
|
|
61
|
+
*/
|
|
62
|
+
private scheduleRetry;
|
|
63
|
+
/**
|
|
64
|
+
* Stop recording and clean up resources
|
|
65
|
+
*/
|
|
66
|
+
stop(): void;
|
|
38
67
|
/**
|
|
39
68
|
* Get current session ID
|
|
40
69
|
*/
|
|
@@ -49,6 +78,14 @@ export declare class BentoLabsSDK {
|
|
|
49
78
|
getConfig(): Omit<SDKConfig, 'apiKey'> & {
|
|
50
79
|
apiKey: string;
|
|
51
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* Get current event queue length
|
|
83
|
+
*/
|
|
84
|
+
getEventQueueLength(): number;
|
|
85
|
+
/**
|
|
86
|
+
* Manually trigger a batch send
|
|
87
|
+
*/
|
|
88
|
+
flushEvents(): Promise<void>;
|
|
52
89
|
}
|
|
53
90
|
declare const _default: BentoLabsSDK;
|
|
54
91
|
export default _default;
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BentoLabsSDK = void 0;
|
|
4
|
+
const rrweb_1 = require("rrweb");
|
|
4
5
|
class BentoLabsSDK {
|
|
5
6
|
constructor() {
|
|
6
7
|
this.config = {
|
|
7
8
|
apiKey: '',
|
|
8
9
|
endpoint: '',
|
|
9
10
|
debug: false,
|
|
11
|
+
batchSize: 50,
|
|
12
|
+
batchInterval: 10000, // 10 seconds
|
|
13
|
+
enableRecording: true,
|
|
14
|
+
maxRetries: 3,
|
|
15
|
+
baseRetryDelay: 1000,
|
|
10
16
|
};
|
|
11
17
|
this.sessionId = '';
|
|
12
18
|
this.events = [];
|
|
13
19
|
this.isRecording = false;
|
|
20
|
+
this.batchTimer = null;
|
|
21
|
+
this.retryTimer = null;
|
|
22
|
+
this.stopRecording = null;
|
|
14
23
|
}
|
|
15
24
|
/**
|
|
16
25
|
* Initialize the BentoLabs SDK
|
|
@@ -23,6 +32,11 @@ class BentoLabsSDK {
|
|
|
23
32
|
const defaultOptions = {
|
|
24
33
|
endpoint: 'https://api.bentolabs.ai', // Replace with your actual API URL
|
|
25
34
|
debug: false,
|
|
35
|
+
batchSize: 50,
|
|
36
|
+
batchInterval: 10000, // 10 seconds
|
|
37
|
+
enableRecording: true,
|
|
38
|
+
maxRetries: 3,
|
|
39
|
+
baseRetryDelay: 1000, // 1 second
|
|
26
40
|
};
|
|
27
41
|
const mergedOptions = Object.assign(Object.assign(Object.assign({}, defaultOptions), options), {
|
|
28
42
|
// Ensure debug is always a boolean
|
|
@@ -32,6 +46,11 @@ class BentoLabsSDK {
|
|
|
32
46
|
apiKey,
|
|
33
47
|
endpoint: mergedOptions.endpoint,
|
|
34
48
|
debug: mergedOptions.debug,
|
|
49
|
+
batchSize: mergedOptions.batchSize,
|
|
50
|
+
batchInterval: mergedOptions.batchInterval,
|
|
51
|
+
enableRecording: mergedOptions.enableRecording,
|
|
52
|
+
maxRetries: mergedOptions.maxRetries,
|
|
53
|
+
baseRetryDelay: mergedOptions.baseRetryDelay,
|
|
35
54
|
};
|
|
36
55
|
// Generate session ID with 'sess_' prefix
|
|
37
56
|
this.sessionId = this.generateSessionId();
|
|
@@ -69,11 +88,39 @@ class BentoLabsSDK {
|
|
|
69
88
|
* Start recording user interactions
|
|
70
89
|
*/
|
|
71
90
|
startRecording() {
|
|
91
|
+
if (!this.config.enableRecording) {
|
|
92
|
+
if (this.config.debug) {
|
|
93
|
+
console.log('[BentoLabsSDK] Recording disabled in configuration');
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
72
97
|
if (this.config.debug) {
|
|
73
98
|
console.log('[BentoLabsSDK] Starting recording for session:', this.sessionId);
|
|
74
99
|
}
|
|
75
|
-
|
|
76
|
-
|
|
100
|
+
try {
|
|
101
|
+
const stopFn = (0, rrweb_1.record)({
|
|
102
|
+
emit: (event) => {
|
|
103
|
+
this.addEvent({
|
|
104
|
+
timestamp: event.timestamp,
|
|
105
|
+
type: event.type.toString(),
|
|
106
|
+
data: event,
|
|
107
|
+
sessionId: this.sessionId,
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
recordCanvas: true,
|
|
111
|
+
collectFonts: true,
|
|
112
|
+
plugins: [],
|
|
113
|
+
});
|
|
114
|
+
this.stopRecording = stopFn || null;
|
|
115
|
+
this.isRecording = true;
|
|
116
|
+
if (this.config.debug) {
|
|
117
|
+
console.log('[BentoLabsSDK] Recording started successfully');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
console.error('[BentoLabsSDK] Failed to start recording:', error);
|
|
122
|
+
this.isRecording = false;
|
|
123
|
+
}
|
|
77
124
|
}
|
|
78
125
|
/**
|
|
79
126
|
* Start batching events for transmission
|
|
@@ -82,7 +129,156 @@ class BentoLabsSDK {
|
|
|
82
129
|
if (this.config.debug) {
|
|
83
130
|
console.log('[BentoLabsSDK] Starting event batching');
|
|
84
131
|
}
|
|
85
|
-
//
|
|
132
|
+
// Clear any existing timer
|
|
133
|
+
if (this.batchTimer) {
|
|
134
|
+
clearInterval(this.batchTimer);
|
|
135
|
+
}
|
|
136
|
+
// Set up periodic batch sending
|
|
137
|
+
this.batchTimer = setInterval(() => {
|
|
138
|
+
this.sendBatch();
|
|
139
|
+
}, this.config.batchInterval);
|
|
140
|
+
if (this.config.debug) {
|
|
141
|
+
console.log(`[BentoLabsSDK] Batch timer set for ${this.config.batchInterval}ms intervals`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Add an event to the events array
|
|
146
|
+
*/
|
|
147
|
+
addEvent(event) {
|
|
148
|
+
this.events.push(event);
|
|
149
|
+
if (this.config.debug) {
|
|
150
|
+
console.log('[BentoLabsSDK] Event added:', event.type);
|
|
151
|
+
}
|
|
152
|
+
// Check if we should send a batch based on size
|
|
153
|
+
if (this.events.length >= this.config.batchSize) {
|
|
154
|
+
if (this.config.debug) {
|
|
155
|
+
console.log(`[BentoLabsSDK] Batch size limit reached (${this.config.batchSize}), sending batch`);
|
|
156
|
+
}
|
|
157
|
+
this.sendBatch();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Send batched events to the API with exponential backoff retry
|
|
162
|
+
*/
|
|
163
|
+
async sendBatch() {
|
|
164
|
+
// Filter out events that are not ready for retry yet
|
|
165
|
+
const now = Date.now();
|
|
166
|
+
const readyEvents = this.events.filter(event => {
|
|
167
|
+
if ('nextRetryTime' in event) {
|
|
168
|
+
return now >= event.nextRetryTime;
|
|
169
|
+
}
|
|
170
|
+
return true;
|
|
171
|
+
});
|
|
172
|
+
if (readyEvents.length === 0) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
// Remove ready events from the main array
|
|
176
|
+
this.events = this.events.filter(event => !readyEvents.includes(event));
|
|
177
|
+
const payload = {
|
|
178
|
+
sessionId: this.sessionId,
|
|
179
|
+
events: readyEvents,
|
|
180
|
+
timestamp: now,
|
|
181
|
+
userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'Unknown',
|
|
182
|
+
url: typeof window !== 'undefined' ? window.location.href : 'Unknown',
|
|
183
|
+
};
|
|
184
|
+
if (this.config.debug) {
|
|
185
|
+
console.log(`[BentoLabsSDK] Sending batch with ${readyEvents.length} events`);
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
const response = await fetch(`${this.config.endpoint}/events`, {
|
|
189
|
+
method: 'POST',
|
|
190
|
+
headers: {
|
|
191
|
+
'Content-Type': 'application/json',
|
|
192
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
193
|
+
'X-Session-ID': this.sessionId,
|
|
194
|
+
},
|
|
195
|
+
body: JSON.stringify(payload),
|
|
196
|
+
});
|
|
197
|
+
if (!response.ok) {
|
|
198
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
199
|
+
}
|
|
200
|
+
if (this.config.debug) {
|
|
201
|
+
console.log('[BentoLabsSDK] Batch sent successfully');
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
console.error('[BentoLabsSDK] Failed to send batch:', error);
|
|
206
|
+
// Convert events to retryable events with exponential backoff
|
|
207
|
+
const retryableEvents = readyEvents.map(event => {
|
|
208
|
+
const retryCount = 'retryCount' in event ? event.retryCount + 1 : 1;
|
|
209
|
+
const delay = this.config.baseRetryDelay * Math.pow(2, retryCount - 1);
|
|
210
|
+
return Object.assign(Object.assign({}, event), { retryCount, nextRetryTime: now + delay });
|
|
211
|
+
});
|
|
212
|
+
// Filter out events that have exceeded max retries
|
|
213
|
+
const eventsToRetry = retryableEvents.filter(event => event.retryCount <= this.config.maxRetries);
|
|
214
|
+
const droppedEvents = retryableEvents.length - eventsToRetry.length;
|
|
215
|
+
if (droppedEvents > 0 && this.config.debug) {
|
|
216
|
+
console.log(`[BentoLabsSDK] Dropped ${droppedEvents} events after max retries`);
|
|
217
|
+
}
|
|
218
|
+
// Re-add events that can still be retried
|
|
219
|
+
this.events.unshift(...eventsToRetry);
|
|
220
|
+
if (this.config.debug && eventsToRetry.length > 0) {
|
|
221
|
+
const nextRetryIn = Math.min(...eventsToRetry.map(e => e.nextRetryTime)) - now;
|
|
222
|
+
console.log(`[BentoLabsSDK] ${eventsToRetry.length} events re-queued for retry in ${nextRetryIn}ms`);
|
|
223
|
+
}
|
|
224
|
+
// Set up retry timer if we have events to retry
|
|
225
|
+
this.scheduleRetry();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Schedule retry attempts for failed events
|
|
230
|
+
*/
|
|
231
|
+
scheduleRetry() {
|
|
232
|
+
// Clear existing retry timer
|
|
233
|
+
if (this.retryTimer) {
|
|
234
|
+
clearTimeout(this.retryTimer);
|
|
235
|
+
this.retryTimer = null;
|
|
236
|
+
}
|
|
237
|
+
// Find the next retry time
|
|
238
|
+
const now = Date.now();
|
|
239
|
+
const retryableEvents = this.events.filter(event => 'nextRetryTime' in event);
|
|
240
|
+
if (retryableEvents.length === 0) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const nextRetryTime = Math.min(...retryableEvents.map(e => e.nextRetryTime));
|
|
244
|
+
const delay = Math.max(0, nextRetryTime - now);
|
|
245
|
+
if (this.config.debug) {
|
|
246
|
+
console.log(`[BentoLabsSDK] Scheduling retry in ${delay}ms`);
|
|
247
|
+
}
|
|
248
|
+
this.retryTimer = setTimeout(() => {
|
|
249
|
+
this.sendBatch();
|
|
250
|
+
}, delay);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Stop recording and clean up resources
|
|
254
|
+
*/
|
|
255
|
+
stop() {
|
|
256
|
+
if (this.config.debug) {
|
|
257
|
+
console.log('[BentoLabsSDK] Stopping recording and batching');
|
|
258
|
+
}
|
|
259
|
+
// Stop rrweb recording
|
|
260
|
+
if (this.stopRecording) {
|
|
261
|
+
this.stopRecording();
|
|
262
|
+
this.stopRecording = null;
|
|
263
|
+
}
|
|
264
|
+
// Clear batch timer
|
|
265
|
+
if (this.batchTimer) {
|
|
266
|
+
clearInterval(this.batchTimer);
|
|
267
|
+
this.batchTimer = null;
|
|
268
|
+
}
|
|
269
|
+
// Clear retry timer
|
|
270
|
+
if (this.retryTimer) {
|
|
271
|
+
clearTimeout(this.retryTimer);
|
|
272
|
+
this.retryTimer = null;
|
|
273
|
+
}
|
|
274
|
+
// Send any remaining events
|
|
275
|
+
if (this.events.length > 0) {
|
|
276
|
+
this.sendBatch();
|
|
277
|
+
}
|
|
278
|
+
this.isRecording = false;
|
|
279
|
+
if (this.config.debug) {
|
|
280
|
+
console.log('[BentoLabsSDK] Stopped successfully');
|
|
281
|
+
}
|
|
86
282
|
}
|
|
87
283
|
/**
|
|
88
284
|
* Get current session ID
|
|
@@ -106,8 +302,25 @@ class BentoLabsSDK {
|
|
|
106
302
|
: '',
|
|
107
303
|
endpoint: this.config.endpoint,
|
|
108
304
|
debug: this.config.debug,
|
|
305
|
+
batchSize: this.config.batchSize,
|
|
306
|
+
batchInterval: this.config.batchInterval,
|
|
307
|
+
enableRecording: this.config.enableRecording,
|
|
308
|
+
maxRetries: this.config.maxRetries,
|
|
309
|
+
baseRetryDelay: this.config.baseRetryDelay,
|
|
109
310
|
};
|
|
110
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* Get current event queue length
|
|
314
|
+
*/
|
|
315
|
+
getEventQueueLength() {
|
|
316
|
+
return this.events.length;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Manually trigger a batch send
|
|
320
|
+
*/
|
|
321
|
+
async flushEvents() {
|
|
322
|
+
await this.sendBatch();
|
|
323
|
+
}
|
|
111
324
|
}
|
|
112
325
|
exports.BentoLabsSDK = BentoLabsSDK;
|
|
113
326
|
// Export a default instance for convenience
|