@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 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
- this.isRecording = true;
76
- // TODO: Implement rrweb recording logic
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
- // TODO: Implement event batching logic
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bentolabs/sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {