@chirpier/chirpier-js 0.1.2 → 0.1.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/dist/index.d.ts CHANGED
@@ -1,16 +1,17 @@
1
+ export declare enum LogLevel {
2
+ None = 0,
3
+ Error = 1,
4
+ Info = 2,
5
+ Debug = 3
6
+ }
1
7
  interface Options {
2
8
  key: string;
3
- apiEndpoint?: string;
4
- retries?: number;
5
- timeout?: number;
6
- batchSize?: number;
7
- flushInterval?: number;
9
+ logLevel?: LogLevel;
8
10
  }
9
11
  export interface Event {
10
12
  group_id: string;
11
- stream: string;
13
+ stream_name: string;
12
14
  value: number;
13
- event_id?: string;
14
15
  }
15
16
  export declare class ChirpierError extends Error {
16
17
  constructor(message: string);
@@ -19,20 +20,30 @@ export declare class ChirpierError extends Error {
19
20
  * Main Chirpier class for monitoring events.
20
21
  */
21
22
  export declare class Chirpier {
23
+ private static instance;
22
24
  private readonly apiKey;
23
25
  private readonly apiEndpoint;
24
26
  private readonly retries;
25
27
  private readonly timeout;
26
28
  private readonly axiosInstance;
27
29
  private eventQueue;
28
- private flushTimeout;
29
30
  private readonly batchSize;
30
- private readonly flushInterval;
31
+ private readonly flushDelay;
32
+ private flushTimeoutId;
33
+ private readonly queueLock;
34
+ private readonly flushLock;
35
+ private readonly logLevel;
31
36
  /**
32
37
  * Initializes a new instance of the Chirpier class.
33
38
  * @param options - Configuration options for the SDK.
34
39
  */
35
- constructor({ key, apiEndpoint, retries, timeout, batchSize, flushInterval, }: Options);
40
+ private constructor();
41
+ /**
42
+ * Gets the singleton instance of Chirpier, creating it if it doesn't exist.
43
+ * @param options - Configuration options for the SDK.
44
+ * @returns The Chirpier instance.
45
+ */
46
+ static getInstance(options: Options): Chirpier | null;
36
47
  /**
37
48
  * Validates the event structure.
38
49
  * @param event - The event to validate.
@@ -53,6 +64,7 @@ export declare class Chirpier {
53
64
  * @param events - The array of events to send.
54
65
  */
55
66
  private sendEvents;
67
+ static stop(): Promise<void>;
56
68
  }
57
69
  /**
58
70
  * Initializes the Chirpier SDK.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,UAAU,OAAO;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAGD,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAGD,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAK5B;AAED;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,UAAU,CAAe;IACjC,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC;;;OAGG;gBACS,EACV,GAAG,EACH,WAAkC,EAClC,OAAyB,EACzB,OAAyB,EACzB,SAAe,EACf,aAAmB,GACpB,EAAE,OAAO;IAyCV;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAapB;;;OAGG;IACU,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BjD;;OAEG;YACW,UAAU;IA6BxB;;;OAGG;YACW,UAAU;CAGzB;AAwCD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkBjD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAS1C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,oBAAY,QAAQ;IAClB,IAAI,IAAI;IACR,KAAK,IAAI;IACT,IAAI,IAAI;IACR,KAAK,IAAI;CACV;AAGD,UAAU,OAAO;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAGD,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAGD,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAK5B;AAQD;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAyB;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IAEpC;;;OAGG;IACH,OAAO;IA6CP;;;;OAIG;WACW,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI;IAO5D;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAapB;;;OAGG;IACU,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBjD;;OAEG;YACW,UAAU;IA8DxB;;;OAGG;YACW,UAAU;WAKJ,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAa1C;AAqCD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsBjD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAW1C"}
package/dist/index.js CHANGED
@@ -14,17 +14,6 @@ var __extends = (this && this.__extends) || (function () {
14
14
  d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
15
  };
16
16
  })();
17
- var __assign = (this && this.__assign) || function () {
18
- __assign = Object.assign || function(t) {
19
- for (var s, i = 1, n = arguments.length; i < n; i++) {
20
- s = arguments[i];
21
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
22
- t[p] = s[p];
23
- }
24
- return t;
25
- };
26
- return __assign.apply(this, arguments);
27
- };
28
17
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
29
18
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
30
19
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -74,13 +63,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
74
63
  return (mod && mod.__esModule) ? mod : { "default": mod };
75
64
  };
76
65
  Object.defineProperty(exports, "__esModule", { value: true });
77
- exports.monitor = exports.initialize = exports.Chirpier = exports.ChirpierError = void 0;
66
+ exports.monitor = exports.initialize = exports.Chirpier = exports.ChirpierError = exports.LogLevel = void 0;
78
67
  // Import necessary dependencies
79
68
  var axios_1 = __importDefault(require("axios"));
80
69
  var axios_retry_1 = __importDefault(require("axios-retry"));
81
- var uuid_1 = require("@lukeed/uuid");
82
70
  var js_base64_1 = require("js-base64");
83
71
  var constants_1 = require("./constants");
72
+ var async_lock_1 = __importDefault(require("async-lock"));
73
+ // Define logging levels
74
+ var LogLevel;
75
+ (function (LogLevel) {
76
+ LogLevel[LogLevel["None"] = 0] = "None";
77
+ LogLevel[LogLevel["Error"] = 1] = "Error";
78
+ LogLevel[LogLevel["Info"] = 2] = "Info";
79
+ LogLevel[LogLevel["Debug"] = 3] = "Debug";
80
+ })(LogLevel = exports.LogLevel || (exports.LogLevel = {}));
84
81
  // Custom error class for Chirpier-specific errors
85
82
  var ChirpierError = /** @class */ (function (_super) {
86
83
  __extends(ChirpierError, _super);
@@ -101,19 +98,22 @@ var Chirpier = /** @class */ (function () {
101
98
  * Initializes a new instance of the Chirpier class.
102
99
  * @param options - Configuration options for the SDK.
103
100
  */
104
- function Chirpier(_a) {
105
- var key = _a.key, _b = _a.apiEndpoint, apiEndpoint = _b === void 0 ? constants_1.DEFAULT_API_ENDPOINT : _b, _c = _a.retries, retries = _c === void 0 ? constants_1.DEFAULT_RETRIES : _c, _d = _a.timeout, timeout = _d === void 0 ? constants_1.DEFAULT_TIMEOUT : _d, _e = _a.batchSize, batchSize = _e === void 0 ? 100 : _e, _f = _a.flushInterval, flushInterval = _f === void 0 ? 500 : _f;
101
+ function Chirpier(options) {
106
102
  this.eventQueue = [];
107
- this.flushTimeout = null;
103
+ this.flushTimeoutId = null;
104
+ this.queueLock = new async_lock_1.default();
105
+ this.flushLock = new async_lock_1.default();
106
+ var key = options.key, _a = options.logLevel, logLevel = _a === void 0 ? LogLevel.None : _a;
108
107
  if (!key || typeof key !== "string") {
109
108
  throw new ChirpierError("API key is required and must be a string");
110
109
  }
111
110
  this.apiKey = key;
112
- this.apiEndpoint = apiEndpoint;
113
- this.retries = retries;
114
- this.timeout = timeout;
115
- this.batchSize = batchSize;
116
- this.flushInterval = flushInterval;
111
+ this.apiEndpoint = constants_1.DEFAULT_API_ENDPOINT;
112
+ this.retries = constants_1.DEFAULT_RETRIES;
113
+ this.timeout = constants_1.DEFAULT_TIMEOUT;
114
+ this.batchSize = constants_1.DEFAULT_BATCH_SIZE;
115
+ this.flushDelay = constants_1.DEFAULT_FLUSH_DELAY;
116
+ this.logLevel = logLevel;
117
117
  // Create axios instance with authorization header
118
118
  this.axiosInstance = axios_1.default.create({
119
119
  headers: { Authorization: "Bearer ".concat(this.apiKey) },
@@ -136,6 +136,17 @@ var Chirpier = /** @class */ (function () {
136
136
  shouldResetTimeout: true,
137
137
  });
138
138
  }
139
+ /**
140
+ * Gets the singleton instance of Chirpier, creating it if it doesn't exist.
141
+ * @param options - Configuration options for the SDK.
142
+ * @returns The Chirpier instance.
143
+ */
144
+ Chirpier.getInstance = function (options) {
145
+ if (!Chirpier.instance && options.key) {
146
+ Chirpier.instance = new Chirpier(options);
147
+ }
148
+ return Chirpier.instance;
149
+ };
139
150
  /**
140
151
  * Validates the event structure.
141
152
  * @param event - The event to validate.
@@ -145,8 +156,8 @@ var Chirpier = /** @class */ (function () {
145
156
  return (typeof event.group_id === "string" &&
146
157
  /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(event.group_id) &&
147
158
  event.group_id.trim().length > 0 &&
148
- typeof event.stream === "string" &&
149
- event.stream.trim().length > 0 &&
159
+ typeof event.stream_name === "string" &&
160
+ event.stream_name.trim().length > 0 &&
150
161
  typeof event.value === "number");
151
162
  };
152
163
  /**
@@ -155,24 +166,36 @@ var Chirpier = /** @class */ (function () {
155
166
  */
156
167
  Chirpier.prototype.monitor = function (event) {
157
168
  return __awaiter(this, void 0, void 0, function () {
158
- var eventWithID;
159
169
  var _this = this;
160
170
  return __generator(this, function (_a) {
161
- if (!this.apiKey) {
162
- throw new ChirpierError("Chirpier SDK must be initialized before calling monitor()");
163
- }
164
- if (!this.isValidEvent(event)) {
165
- throw new ChirpierError("Invalid event format. Must include group_id, stream, and numeric value.");
166
- }
167
- eventWithID = __assign(__assign({}, event), { event_id: event.event_id || (0, uuid_1.v4)() });
168
- this.eventQueue.push(eventWithID);
169
- if (this.eventQueue.length >= this.batchSize) {
170
- this.flushQueue();
171
- }
172
- else if (!this.flushTimeout) {
173
- this.flushTimeout = setTimeout(function () { return _this.flushQueue(); }, this.flushInterval);
171
+ switch (_a.label) {
172
+ case 0:
173
+ if (!this.isValidEvent(event)) {
174
+ throw new ChirpierError("Invalid event format. Must include group_id, stream_name, and numeric value.");
175
+ }
176
+ return [4 /*yield*/, this.queueLock.acquire("queue", function () { return __awaiter(_this, void 0, void 0, function () {
177
+ return __generator(this, function (_a) {
178
+ if (this.eventQueue.length >= constants_1.MAX_QUEUE_SIZE) {
179
+ throw new ChirpierError("Event queue is full.");
180
+ }
181
+ this.eventQueue.push({ event: event, timestamp: Date.now(), retryCount: 0 });
182
+ return [2 /*return*/];
183
+ });
184
+ }); })];
185
+ case 1:
186
+ _a.sent();
187
+ if (!(this.eventQueue.length >= this.batchSize)) return [3 /*break*/, 3];
188
+ return [4 /*yield*/, this.flushQueue()];
189
+ case 2:
190
+ _a.sent();
191
+ return [3 /*break*/, 4];
192
+ case 3:
193
+ if (!this.flushTimeoutId) {
194
+ this.flushTimeoutId = setTimeout(function () { return _this.flushQueue(); }, this.flushDelay);
195
+ }
196
+ _a.label = 4;
197
+ case 4: return [2 /*return*/];
174
198
  }
175
- return [2 /*return*/];
176
199
  });
177
200
  });
178
201
  };
@@ -181,37 +204,88 @@ var Chirpier = /** @class */ (function () {
181
204
  */
182
205
  Chirpier.prototype.flushQueue = function () {
183
206
  return __awaiter(this, void 0, void 0, function () {
184
- var eventsToSend, error_1;
185
207
  var _this = this;
186
208
  return __generator(this, function (_a) {
187
209
  switch (_a.label) {
188
- case 0:
189
- if (this.eventQueue.length === 0) {
190
- return [2 /*return*/];
191
- }
192
- if (this.flushTimeout) {
193
- clearTimeout(this.flushTimeout);
194
- this.flushTimeout = null;
195
- }
196
- eventsToSend = __spreadArray([], this.eventQueue, true);
197
- this.eventQueue = [];
198
- _a.label = 1;
210
+ case 0:
211
+ // Acquire the flush lock
212
+ return [4 /*yield*/, this.flushLock.acquire("flush", function () { return __awaiter(_this, void 0, void 0, function () {
213
+ var eventsToSend, error_1, retryableEvents_1, _i, eventsToSend_1, queuedEvent;
214
+ var _this = this;
215
+ return __generator(this, function (_a) {
216
+ switch (_a.label) {
217
+ case 0:
218
+ eventsToSend = [];
219
+ // Extract events from the queue under the queue lock
220
+ return [4 /*yield*/, this.queueLock.acquire("eventQueue", function () { return __awaiter(_this, void 0, void 0, function () {
221
+ return __generator(this, function (_a) {
222
+ if (this.eventQueue.length > 0) {
223
+ eventsToSend = __spreadArray([], this.eventQueue, true);
224
+ this.eventQueue = [];
225
+ }
226
+ return [2 /*return*/];
227
+ });
228
+ }); })];
229
+ case 1:
230
+ // Extract events from the queue under the queue lock
231
+ _a.sent();
232
+ if (eventsToSend.length === 0) {
233
+ return [2 /*return*/];
234
+ }
235
+ _a.label = 2;
236
+ case 2:
237
+ _a.trys.push([2, 4, , 6]);
238
+ // Clear any pending flush timeout
239
+ if (this.flushTimeoutId) {
240
+ clearTimeout(this.flushTimeoutId);
241
+ this.flushTimeoutId = null;
242
+ }
243
+ // Attempt to send events
244
+ return [4 /*yield*/, this.sendEvents(eventsToSend.map(function (qe) { return qe.event; }))];
245
+ case 3:
246
+ // Attempt to send events
247
+ _a.sent();
248
+ if (this.logLevel >= LogLevel.Info) {
249
+ console.info("Successfully sent ".concat(eventsToSend.length, " events"));
250
+ }
251
+ return [3 /*break*/, 6];
252
+ case 4:
253
+ error_1 = _a.sent();
254
+ // Log failure
255
+ if (this.logLevel >= LogLevel.Error) {
256
+ console.error("Failed to send events:", error_1);
257
+ }
258
+ retryableEvents_1 = [];
259
+ for (_i = 0, eventsToSend_1 = eventsToSend; _i < eventsToSend_1.length; _i++) {
260
+ queuedEvent = eventsToSend_1[_i];
261
+ if (queuedEvent.retryCount >= this.retries) {
262
+ if (this.logLevel >= LogLevel.Error) {
263
+ console.error("Dropping event after ".concat(this.retries, " retries:"), queuedEvent.event);
264
+ }
265
+ continue; // Skip adding this event back to the queue
266
+ }
267
+ // Increment retry count and add back to the queue
268
+ queuedEvent.retryCount++;
269
+ retryableEvents_1.push(queuedEvent);
270
+ }
271
+ // Requeue remaining retryable events
272
+ return [4 /*yield*/, this.queueLock.acquire("eventQueue", function () { return __awaiter(_this, void 0, void 0, function () {
273
+ return __generator(this, function (_a) {
274
+ this.eventQueue = __spreadArray(__spreadArray([], retryableEvents_1, true), this.eventQueue, true);
275
+ return [2 /*return*/];
276
+ });
277
+ }); })];
278
+ case 5:
279
+ // Requeue remaining retryable events
280
+ _a.sent();
281
+ return [3 /*break*/, 6];
282
+ case 6: return [2 /*return*/];
283
+ }
284
+ });
285
+ }); })];
199
286
  case 1:
200
- _a.trys.push([1, 3, , 4]);
201
- return [4 /*yield*/, this.sendEvents(eventsToSend)];
202
- case 2:
287
+ // Acquire the flush lock
203
288
  _a.sent();
204
- console.info("Successfully sent ".concat(eventsToSend.length, " events"));
205
- return [3 /*break*/, 4];
206
- case 3:
207
- error_1 = _a.sent();
208
- console.error("Failed to send events:", error_1);
209
- return [3 /*break*/, 4];
210
- case 4:
211
- // Schedule next flush if there are more events
212
- if (this.eventQueue.length > 0) {
213
- this.flushTimeout = setTimeout(function () { return _this.flushQueue(); }, this.flushInterval);
214
- }
215
289
  return [2 /*return*/];
216
290
  }
217
291
  });
@@ -233,6 +307,32 @@ var Chirpier = /** @class */ (function () {
233
307
  });
234
308
  });
235
309
  };
310
+ // Stop the timeout and uninitialize the Chirpier instance
311
+ Chirpier.stop = function () {
312
+ return __awaiter(this, void 0, void 0, function () {
313
+ return __generator(this, function (_a) {
314
+ switch (_a.label) {
315
+ case 0:
316
+ if (!Chirpier.instance) {
317
+ return [2 /*return*/];
318
+ }
319
+ if (Chirpier.instance.flushTimeoutId) {
320
+ clearTimeout(Chirpier.instance.flushTimeoutId);
321
+ Chirpier.instance.flushTimeoutId = null;
322
+ }
323
+ // Flush any remaining events in the queue
324
+ return [4 /*yield*/, Chirpier.instance.flushQueue()];
325
+ case 1:
326
+ // Flush any remaining events in the queue
327
+ _a.sent();
328
+ // Uninitialize the Chirpier instance
329
+ Chirpier.instance = null;
330
+ return [2 /*return*/];
331
+ }
332
+ });
333
+ });
334
+ };
335
+ Chirpier.instance = null;
236
336
  return Chirpier;
237
337
  }());
238
338
  exports.Chirpier = Chirpier;
@@ -270,8 +370,6 @@ function isValidJWT(token) {
270
370
  return false;
271
371
  }
272
372
  }
273
- // Singleton instance of Chirpier
274
- var chirpierInstance = null;
275
373
  /**
276
374
  * Initializes the Chirpier SDK.
277
375
  * @param options - Configuration options for the SDK.
@@ -281,14 +379,18 @@ function initialize(options) {
281
379
  throw new ChirpierError("Invalid API key: Not a valid JWT");
282
380
  }
283
381
  try {
284
- chirpierInstance = new Chirpier(options);
382
+ Chirpier.getInstance(options);
285
383
  }
286
384
  catch (error) {
287
385
  if (error instanceof ChirpierError) {
288
- console.error("Failed to initialize Chirpier SDK:", error.message);
386
+ if (options.logLevel && options.logLevel >= LogLevel.Error) {
387
+ console.error("Failed to initialize Chirpier SDK:", error.message);
388
+ }
289
389
  }
290
390
  else {
291
- console.error("An unexpected error occurred during Chirpier SDK initialization:", error);
391
+ if (options.logLevel && options.logLevel >= LogLevel.Error) {
392
+ console.error("An unexpected error occurred during Chirpier SDK initialization:", error);
393
+ }
292
394
  }
293
395
  throw error;
294
396
  }
@@ -299,10 +401,11 @@ exports.initialize = initialize;
299
401
  * @param event - The event to monitor.
300
402
  */
301
403
  function monitor(event) {
302
- if (!chirpierInstance) {
404
+ var instance = Chirpier.getInstance({});
405
+ if (!instance) {
303
406
  throw new ChirpierError("Chirpier SDK is not initialized. Please call initialize() first.");
304
407
  }
305
- chirpierInstance.monitor(event).catch(function (error) {
408
+ instance.monitor(event).catch(function (error) {
306
409
  console.error("Error in monitor function:", error);
307
410
  });
308
411
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chirpier/chirpier-js",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Chirpier SDK for JavaScript",
5
5
  "keywords": [
6
6
  "chirpier",
@@ -27,7 +27,7 @@
27
27
  "test": "jest --coverage"
28
28
  },
29
29
  "dependencies": {
30
- "@lukeed/uuid": "^2.0.1",
30
+ "async-lock": "^1.4.1",
31
31
  "axios": "^0.24.0",
32
32
  "axios-retry": "^4.5.0",
33
33
  "js-base64": "^3.7.7",
@@ -35,15 +35,20 @@
35
35
  "tslib": "^2.3.0"
36
36
  },
37
37
  "devDependencies": {
38
+ "@eslint/js": "^9.15.0",
38
39
  "@jest/globals": "^29.7.0",
40
+ "@types/async-lock": "^1.4.2",
39
41
  "@types/jest": "^29.5.13",
40
42
  "@types/mocha": "^10.0.8",
41
43
  "@types/node": "^22.7.5",
42
44
  "@types/uuid": "^10.0.0",
43
45
  "axios-mock-adapter": "^2.0.0",
46
+ "eslint": "^9.15.0",
47
+ "globals": "^15.12.0",
44
48
  "jest": "^29.7.0",
45
49
  "ts-jest": "^29.2.4",
46
50
  "typescript": "^4.4.3",
51
+ "typescript-eslint": "^8.15.0",
47
52
  "webpack": "^5.52.0",
48
53
  "webpack-cli": "^4.8.0"
49
54
  }
@@ -0,0 +1,170 @@
1
+ import { Chirpier, ChirpierError, Event, LogLevel, initialize, monitor } from "../index";
2
+ import {
3
+ DEFAULT_API_ENDPOINT,
4
+ DEFAULT_RETRIES,
5
+ DEFAULT_TIMEOUT,
6
+ DEFAULT_BATCH_SIZE,
7
+ DEFAULT_FLUSH_DELAY,
8
+ } from "../constants";
9
+ import MockAdapter from "axios-mock-adapter";
10
+ import axios from "axios";
11
+
12
+ describe("Chirpier SDK", () => {
13
+ describe("Initialization", () => {
14
+ test("should throw error if monitor is called before initialize", () => {
15
+ const event: Event = {
16
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
17
+ stream_name: "test-stream",
18
+ value: 1,
19
+ };
20
+
21
+ expect(() => monitor(event)).toThrow(ChirpierError);
22
+ expect(() => monitor(event)).toThrow(
23
+ "Chirpier SDK is not initialized. Please call initialize() first."
24
+ );
25
+ });
26
+
27
+ test("should initialize with default values", () => {
28
+ initialize({
29
+ key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
30
+ logLevel: LogLevel.None
31
+ });
32
+ const chirpier = Chirpier.getInstance({} as any);
33
+
34
+ // Setup mock server
35
+ const mock = new MockAdapter(axios);
36
+ mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
37
+
38
+ expect(chirpier?.["apiEndpoint"]).toBe(DEFAULT_API_ENDPOINT);
39
+ expect(chirpier?.["retries"]).toBe(DEFAULT_RETRIES);
40
+ expect(chirpier?.["timeout"]).toBe(DEFAULT_TIMEOUT);
41
+ expect(chirpier?.["batchSize"]).toBe(DEFAULT_BATCH_SIZE);
42
+ expect(chirpier?.["flushDelay"]).toBe(DEFAULT_FLUSH_DELAY);
43
+
44
+ Chirpier.stop();
45
+ });
46
+
47
+ test("should throw error if key is not provided", () => {
48
+ expect(() => {
49
+ initialize({
50
+ key: "api_key",
51
+ logLevel: LogLevel.None
52
+ });
53
+ }).toThrow(ChirpierError);
54
+ expect(() => {
55
+ initialize({
56
+ key: "api_key",
57
+ logLevel: LogLevel.None
58
+ });
59
+ }).toThrow("Invalid API key: Not a valid JWT");
60
+ });
61
+ });
62
+
63
+ describe("monitor", () => {
64
+ test("event should be sent", async () => {
65
+ // Setup mock server
66
+ const mock = new MockAdapter(axios);
67
+ mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
68
+
69
+ initialize({
70
+ key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
71
+ logLevel: LogLevel.None
72
+ });
73
+
74
+ const event: Event = {
75
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
76
+ stream_name: "test-stream",
77
+ value: 1,
78
+ };
79
+
80
+ monitor(event);
81
+
82
+ await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for flush
83
+
84
+ expect(mock.history.post.length).toBe(1);
85
+ expect(mock.history.post[0].url).toBe(DEFAULT_API_ENDPOINT);
86
+ expect(JSON.parse(mock.history.post[0].data)).toEqual([
87
+ {
88
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
89
+ stream_name: "test-stream",
90
+ value: 1,
91
+ },
92
+ ]);
93
+
94
+ // Clean up the mock
95
+ mock.reset();
96
+ Chirpier.stop();
97
+ });
98
+
99
+ test("should throw error for invalid event", async () => {
100
+ initialize({
101
+ key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
102
+ logLevel: LogLevel.None
103
+ });
104
+ const chirpier = Chirpier.getInstance({} as any);
105
+
106
+ const invalidEvent = {
107
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
108
+ } as any;
109
+ await expect(chirpier?.monitor(invalidEvent)).rejects.toThrow(
110
+ ChirpierError
111
+ );
112
+
113
+ // Clean up the mock
114
+ Chirpier.stop();
115
+ });
116
+
117
+ test("should batch events and flush when batch size is reached", async () => {
118
+ const mock = new MockAdapter(axios);
119
+ mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
120
+
121
+ initialize({
122
+ key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
123
+ logLevel: LogLevel.None
124
+ });
125
+
126
+ const event: Event = {
127
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
128
+ stream_name: "test-stream",
129
+ value: 1,
130
+ };
131
+
132
+ monitor(event);
133
+ monitor(event);
134
+
135
+ await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for flush
136
+
137
+ expect(mock.history.post.length).toBe(1);
138
+ expect(JSON.parse(mock.history.post[0].data).length).toBe(2);
139
+
140
+ mock.reset();
141
+ Chirpier.stop();
142
+ });
143
+
144
+ test("should flush events after interval", async () => {
145
+ const mock = new MockAdapter(axios);
146
+ mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
147
+
148
+ initialize({
149
+ key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
150
+ logLevel: LogLevel.None
151
+ });
152
+
153
+ const event: Event = {
154
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
155
+ stream_name: "test-stream",
156
+ value: 1,
157
+ };
158
+
159
+ monitor(event);
160
+
161
+ await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for flush
162
+
163
+ expect(mock.history.post.length).toBe(1);
164
+ expect(JSON.parse(mock.history.post[0].data).length).toBe(1);
165
+
166
+ mock.reset();
167
+ Chirpier.stop();
168
+ });
169
+ });
170
+ });
package/src/constants.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  // constants.ts
2
2
 
3
- export const DEFAULT_API_ENDPOINT = 'https://events.chirpier.co/api/events';
4
- export const DEFAULT_RETRIES = 30;
5
- export const DEFAULT_TIMEOUT = 5000
3
+ export const DEFAULT_API_ENDPOINT = 'https://events.chirpier.co/v1.0/events';
4
+ export const DEFAULT_RETRIES = 15;
5
+ export const DEFAULT_TIMEOUT = 5000
6
+ export const DEFAULT_BATCH_SIZE = 100;
7
+ export const DEFAULT_FLUSH_DELAY = 500;
8
+ export const MAX_QUEUE_SIZE = 2000;