@chirpier/chirpier-js 0.1.3 → 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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Chirpier SDK
2
2
 
3
- The Chirpier SDK is a lightweight, versatile library for monitoring and tracking streams of data in both browser and server environments. With built-in retry logic, and offline handling, the Chirpier SDK makes it easy to collect and send data to the Chirpier API.
3
+ The Chirpier SDK is a lightweight, library for monitoring and tracking streams of data in both browser and server environments. With built-in retry logic, and offline handling, the Chirpier SDK makes it easy to collect and send data to the Chirpier API.
4
4
 
5
5
  ## Features
6
6
 
@@ -12,6 +12,7 @@ The Chirpier SDK is a lightweight, versatile library for monitoring and tracking
12
12
  ## Installation
13
13
 
14
14
  You can install the Chirpier SDK via npm:
15
+
15
16
  ```
16
17
  npm install @chirpier/chirpier-js
17
18
  ```
@@ -23,8 +24,9 @@ npm install @chirpier/chirpier-js
23
24
  To start using the SDK, you need to initialize it with your API key. The SDK works in both browser and Node.js environments.
24
25
 
25
26
  #### In a Browser
27
+
26
28
  ```
27
- import { initialize, monitor } from '@chirpier/chirpier-js';
29
+ import { initialize, monitor, Event } from '@chirpier/chirpier-js';
28
30
 
29
31
  // Initialize the SDK with your API key
30
32
  initialize({ key: 'your-api-key' });
@@ -34,13 +36,14 @@ monitor({
34
36
  group_id: '02e4f4d8-415e-4fc1-b01a-677ac5bc9207',
35
37
  stream_name: 'Sales',
36
38
  value: 15.30,
37
- });
39
+ } as Event);
38
40
  ```
39
41
 
40
42
  #### In a Server (e.g., Express.js)
43
+
41
44
  ```
42
45
  const express = require('express');
43
- const { initialize, monitor } = require('@chirpier/chirpier-js');
46
+ const { initialize, monitor, Event } = require('@chirpier/chirpier-js');
44
47
 
45
48
  const app = express();
46
49
  const port = 3000;
@@ -50,15 +53,15 @@ initialize({ key: 'your-api-key' });
50
53
 
51
54
  app.use(express.json());
52
55
 
53
- app.post('/track-event', (req, res) => {
54
- const { group_id, monitor, value } = req.body;
56
+ app.post('/monitor', (req, res) => {
57
+ const { group_id, stream_name, value } = req.body;
55
58
 
56
- if (!group_id || !monitor || !value) {
59
+ if (!group_id || !stream_name || !value) {
57
60
  return res.status(400).json({ error: 'Missing required fields' });
58
61
  }
59
62
 
60
63
  // Monitor an event
61
- monitor({ group_id, monitor, value });
64
+ monitor({ group_id, stream_name, value } as Event);
62
65
 
63
66
  res.status(200).json({ message: 'Event tracked successfully' });
64
67
  });
@@ -69,6 +72,7 @@ app.listen(port, () => {
69
72
  ```
70
73
 
71
74
  ## Example
75
+
72
76
  ```
73
77
  // Initialize the SDK with your API key
74
78
  initialize({ key: 'your-api-key' });
@@ -76,12 +80,7 @@ initialize({ key: 'your-api-key' });
76
80
  // Monitor an event
77
81
  monitor({
78
82
  group_id: 'group UUID',
79
- stream_name: 'Sales',
83
+ stream_name: 'My measurement',
80
84
  value: 15.3,
81
85
  });
82
86
  ```
83
-
84
- ## Advanced Usage
85
- Handling Offline Scenarios
86
-
87
- The SDK automatically queues events when the network is unavailable and sends them when the connection is restored.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=chirpier.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chirpier.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/chirpier.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ var index_1 = require("../index");
43
+ var constants_1 = require("../constants");
44
+ var axios_mock_adapter_1 = __importDefault(require("axios-mock-adapter"));
45
+ var axios_1 = __importDefault(require("axios"));
46
+ describe("Chirpier SDK", function () {
47
+ describe("Initialization", function () {
48
+ test("should throw error if monitor is called before initialize", function () {
49
+ var event = {
50
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
51
+ stream_name: "test-stream",
52
+ value: 1,
53
+ };
54
+ expect(function () { return (0, index_1.monitor)(event); }).toThrow(index_1.ChirpierError);
55
+ expect(function () { return (0, index_1.monitor)(event); }).toThrow("Chirpier SDK is not initialized. Please call initialize() first.");
56
+ });
57
+ test("should initialize with default values", function () {
58
+ (0, index_1.initialize)({
59
+ key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
60
+ logLevel: index_1.LogLevel.None
61
+ });
62
+ var chirpier = index_1.Chirpier.getInstance({});
63
+ // Setup mock server
64
+ var mock = new axios_mock_adapter_1.default(axios_1.default);
65
+ mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
66
+ expect(chirpier === null || chirpier === void 0 ? void 0 : chirpier["apiEndpoint"]).toBe(constants_1.DEFAULT_API_ENDPOINT);
67
+ expect(chirpier === null || chirpier === void 0 ? void 0 : chirpier["retries"]).toBe(constants_1.DEFAULT_RETRIES);
68
+ expect(chirpier === null || chirpier === void 0 ? void 0 : chirpier["timeout"]).toBe(constants_1.DEFAULT_TIMEOUT);
69
+ expect(chirpier === null || chirpier === void 0 ? void 0 : chirpier["batchSize"]).toBe(constants_1.DEFAULT_BATCH_SIZE);
70
+ expect(chirpier === null || chirpier === void 0 ? void 0 : chirpier["flushDelay"]).toBe(constants_1.DEFAULT_FLUSH_DELAY);
71
+ index_1.Chirpier.stop();
72
+ });
73
+ test("should throw error if key is not provided", function () {
74
+ expect(function () {
75
+ (0, index_1.initialize)({
76
+ key: "api_key",
77
+ logLevel: index_1.LogLevel.None
78
+ });
79
+ }).toThrow(index_1.ChirpierError);
80
+ expect(function () {
81
+ (0, index_1.initialize)({
82
+ key: "api_key",
83
+ logLevel: index_1.LogLevel.None
84
+ });
85
+ }).toThrow("Invalid API key: Not a valid JWT");
86
+ });
87
+ });
88
+ describe("monitor", function () {
89
+ test("event should be sent", function () { return __awaiter(void 0, void 0, void 0, function () {
90
+ var mock, event;
91
+ return __generator(this, function (_a) {
92
+ switch (_a.label) {
93
+ case 0:
94
+ mock = new axios_mock_adapter_1.default(axios_1.default);
95
+ mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
96
+ (0, index_1.initialize)({
97
+ key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
98
+ logLevel: index_1.LogLevel.None
99
+ });
100
+ event = {
101
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
102
+ stream_name: "test-stream",
103
+ value: 1,
104
+ };
105
+ (0, index_1.monitor)(event);
106
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })];
107
+ case 1:
108
+ _a.sent(); // Wait for flush
109
+ expect(mock.history.post.length).toBe(1);
110
+ expect(mock.history.post[0].url).toBe(constants_1.DEFAULT_API_ENDPOINT);
111
+ expect(JSON.parse(mock.history.post[0].data)).toEqual([
112
+ {
113
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
114
+ stream_name: "test-stream",
115
+ value: 1,
116
+ },
117
+ ]);
118
+ // Clean up the mock
119
+ mock.reset();
120
+ index_1.Chirpier.stop();
121
+ return [2 /*return*/];
122
+ }
123
+ });
124
+ }); });
125
+ test("should throw error for invalid event", function () { return __awaiter(void 0, void 0, void 0, function () {
126
+ var chirpier, invalidEvent;
127
+ return __generator(this, function (_a) {
128
+ switch (_a.label) {
129
+ case 0:
130
+ (0, index_1.initialize)({
131
+ key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
132
+ logLevel: index_1.LogLevel.None
133
+ });
134
+ chirpier = index_1.Chirpier.getInstance({});
135
+ invalidEvent = {
136
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
137
+ };
138
+ return [4 /*yield*/, expect(chirpier === null || chirpier === void 0 ? void 0 : chirpier.monitor(invalidEvent)).rejects.toThrow(index_1.ChirpierError)];
139
+ case 1:
140
+ _a.sent();
141
+ // Clean up the mock
142
+ index_1.Chirpier.stop();
143
+ return [2 /*return*/];
144
+ }
145
+ });
146
+ }); });
147
+ test("should batch events and flush when batch size is reached", function () { return __awaiter(void 0, void 0, void 0, function () {
148
+ var mock, event;
149
+ return __generator(this, function (_a) {
150
+ switch (_a.label) {
151
+ case 0:
152
+ mock = new axios_mock_adapter_1.default(axios_1.default);
153
+ mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
154
+ (0, index_1.initialize)({
155
+ key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
156
+ logLevel: index_1.LogLevel.None
157
+ });
158
+ event = {
159
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
160
+ stream_name: "test-stream",
161
+ value: 1,
162
+ };
163
+ (0, index_1.monitor)(event);
164
+ (0, index_1.monitor)(event);
165
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })];
166
+ case 1:
167
+ _a.sent(); // Wait for flush
168
+ expect(mock.history.post.length).toBe(1);
169
+ expect(JSON.parse(mock.history.post[0].data).length).toBe(2);
170
+ mock.reset();
171
+ index_1.Chirpier.stop();
172
+ return [2 /*return*/];
173
+ }
174
+ });
175
+ }); });
176
+ test("should flush events after interval", function () { return __awaiter(void 0, void 0, void 0, function () {
177
+ var mock, event;
178
+ return __generator(this, function (_a) {
179
+ switch (_a.label) {
180
+ case 0:
181
+ mock = new axios_mock_adapter_1.default(axios_1.default);
182
+ mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
183
+ (0, index_1.initialize)({
184
+ key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
185
+ logLevel: index_1.LogLevel.None
186
+ });
187
+ event = {
188
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
189
+ stream_name: "test-stream",
190
+ value: 1,
191
+ };
192
+ (0, index_1.monitor)(event);
193
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })];
194
+ case 1:
195
+ _a.sent(); // Wait for flush
196
+ expect(mock.history.post.length).toBe(1);
197
+ expect(JSON.parse(mock.history.post[0].data).length).toBe(1);
198
+ mock.reset();
199
+ index_1.Chirpier.stop();
200
+ return [2 /*return*/];
201
+ }
202
+ });
203
+ }); });
204
+ });
205
+ });
@@ -1,6 +1,7 @@
1
1
  export declare const DEFAULT_API_ENDPOINT = "https://events.chirpier.co/v1.0/events";
2
- export declare const DEFAULT_RETRIES = 30;
2
+ export declare const DEFAULT_RETRIES = 15;
3
3
  export declare const DEFAULT_TIMEOUT = 5000;
4
- export declare const DEFAULT_BATCH_SIZE = 50;
4
+ export declare const DEFAULT_BATCH_SIZE = 100;
5
5
  export declare const DEFAULT_FLUSH_DELAY = 500;
6
+ export declare const MAX_QUEUE_SIZE = 2000;
6
7
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,oBAAoB,2CAA2C,CAAC;AAC7E,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,eAAe,OAAO,CAAA;AACnC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,mBAAmB,MAAM,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,oBAAoB,2CAA2C,CAAC;AAC7E,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,eAAe,OAAO,CAAA;AACnC,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,cAAc,OAAO,CAAC"}
package/dist/constants.js CHANGED
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  // constants.ts
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.DEFAULT_FLUSH_DELAY = exports.DEFAULT_BATCH_SIZE = exports.DEFAULT_TIMEOUT = exports.DEFAULT_RETRIES = exports.DEFAULT_API_ENDPOINT = void 0;
4
+ exports.MAX_QUEUE_SIZE = exports.DEFAULT_FLUSH_DELAY = exports.DEFAULT_BATCH_SIZE = exports.DEFAULT_TIMEOUT = exports.DEFAULT_RETRIES = exports.DEFAULT_API_ENDPOINT = void 0;
5
5
  exports.DEFAULT_API_ENDPOINT = 'https://events.chirpier.co/v1.0/events';
6
- exports.DEFAULT_RETRIES = 30;
6
+ exports.DEFAULT_RETRIES = 15;
7
7
  exports.DEFAULT_TIMEOUT = 5000;
8
- exports.DEFAULT_BATCH_SIZE = 50;
8
+ exports.DEFAULT_BATCH_SIZE = 100;
9
9
  exports.DEFAULT_FLUSH_DELAY = 500;
10
+ exports.MAX_QUEUE_SIZE = 2000;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,12 @@
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;
9
+ logLevel?: LogLevel;
3
10
  }
4
11
  export interface Event {
5
12
  group_id: string;
@@ -21,8 +28,11 @@ export declare class Chirpier {
21
28
  private readonly axiosInstance;
22
29
  private eventQueue;
23
30
  private readonly batchSize;
24
- private readonly flushTimeout;
31
+ private readonly flushDelay;
25
32
  private flushTimeoutId;
33
+ private readonly queueLock;
34
+ private readonly flushLock;
35
+ private readonly logLevel;
26
36
  /**
27
37
  * Initializes a new instance of the Chirpier class.
28
38
  * @param options - Configuration options for the SDK.
@@ -54,7 +64,7 @@ export declare class Chirpier {
54
64
  * @param events - The array of events to send.
55
65
  */
56
66
  private sendEvents;
57
- static stop(): void;
67
+ static stop(): Promise<void>;
58
68
  }
59
69
  /**
60
70
  * Initializes the Chirpier SDK.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,UAAU,OAAO;IACf,GAAG,EAAE,MAAM,CAAC;CACb;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;AAED;;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,CAAe;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,cAAc,CAA+B;IAErD;;;OAGG;IACH,OAAO;IA8CP;;;;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;IAiBjD;;OAEG;YACW,UAAU;IAqBxB;;;OAGG;YACW,UAAU;WAKV,IAAI,IAAI,IAAI;CAa3B;AAqCD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkBjD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAW1C"}
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
@@ -63,12 +63,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
63
63
  return (mod && mod.__esModule) ? mod : { "default": mod };
64
64
  };
65
65
  Object.defineProperty(exports, "__esModule", { value: true });
66
- exports.monitor = exports.initialize = exports.Chirpier = exports.ChirpierError = void 0;
66
+ exports.monitor = exports.initialize = exports.Chirpier = exports.ChirpierError = exports.LogLevel = void 0;
67
67
  // Import necessary dependencies
68
68
  var axios_1 = __importDefault(require("axios"));
69
69
  var axios_retry_1 = __importDefault(require("axios-retry"));
70
70
  var js_base64_1 = require("js-base64");
71
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 = {}));
72
81
  // Custom error class for Chirpier-specific errors
73
82
  var ChirpierError = /** @class */ (function (_super) {
74
83
  __extends(ChirpierError, _super);
@@ -92,7 +101,9 @@ var Chirpier = /** @class */ (function () {
92
101
  function Chirpier(options) {
93
102
  this.eventQueue = [];
94
103
  this.flushTimeoutId = null;
95
- var key = options.key;
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;
96
107
  if (!key || typeof key !== "string") {
97
108
  throw new ChirpierError("API key is required and must be a string");
98
109
  }
@@ -101,7 +112,8 @@ var Chirpier = /** @class */ (function () {
101
112
  this.retries = constants_1.DEFAULT_RETRIES;
102
113
  this.timeout = constants_1.DEFAULT_TIMEOUT;
103
114
  this.batchSize = constants_1.DEFAULT_BATCH_SIZE;
104
- this.flushTimeout = constants_1.DEFAULT_FLUSH_DELAY;
115
+ this.flushDelay = constants_1.DEFAULT_FLUSH_DELAY;
116
+ this.logLevel = logLevel;
105
117
  // Create axios instance with authorization header
106
118
  this.axiosInstance = axios_1.default.create({
107
119
  headers: { Authorization: "Bearer ".concat(this.apiKey) },
@@ -161,19 +173,28 @@ var Chirpier = /** @class */ (function () {
161
173
  if (!this.isValidEvent(event)) {
162
174
  throw new ChirpierError("Invalid event format. Must include group_id, stream_name, and numeric value.");
163
175
  }
164
- this.eventQueue.push(event);
165
- if (!(this.eventQueue.length >= this.batchSize)) return [3 /*break*/, 2];
166
- console.info("Batch size reached. Flushing queue.");
167
- return [4 /*yield*/, this.flushQueue()];
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
+ }); })];
168
185
  case 1:
169
186
  _a.sent();
170
- return [3 /*break*/, 3];
187
+ if (!(this.eventQueue.length >= this.batchSize)) return [3 /*break*/, 3];
188
+ return [4 /*yield*/, this.flushQueue()];
171
189
  case 2:
190
+ _a.sent();
191
+ return [3 /*break*/, 4];
192
+ case 3:
172
193
  if (!this.flushTimeoutId) {
173
- this.flushTimeoutId = setTimeout(function () { return _this.flushQueue(); }, this.flushTimeout);
194
+ this.flushTimeoutId = setTimeout(function () { return _this.flushQueue(); }, this.flushDelay);
174
195
  }
175
- _a.label = 3;
176
- case 3: return [2 /*return*/];
196
+ _a.label = 4;
197
+ case 4: return [2 /*return*/];
177
198
  }
178
199
  });
179
200
  });
@@ -183,32 +204,89 @@ var Chirpier = /** @class */ (function () {
183
204
  */
184
205
  Chirpier.prototype.flushQueue = function () {
185
206
  return __awaiter(this, void 0, void 0, function () {
186
- var eventsToSend, error_1;
207
+ var _this = this;
187
208
  return __generator(this, function (_a) {
188
209
  switch (_a.label) {
189
- case 0:
190
- if (this.flushTimeoutId) {
191
- clearTimeout(this.flushTimeoutId);
192
- this.flushTimeoutId = null;
193
- }
194
- if (this.eventQueue.length === 0) {
195
- return [2 /*return*/];
196
- }
197
- eventsToSend = __spreadArray([], this.eventQueue, true);
198
- this.eventQueue = [];
199
- _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
+ }); })];
200
286
  case 1:
201
- _a.trys.push([1, 3, , 4]);
202
- return [4 /*yield*/, this.sendEvents(eventsToSend)];
203
- case 2:
287
+ // Acquire the flush lock
204
288
  _a.sent();
205
- console.info("Successfully sent ".concat(eventsToSend.length, " events"));
206
- return [3 /*break*/, 4];
207
- case 3:
208
- error_1 = _a.sent();
209
- console.error("Failed to send events:", error_1);
210
- return [3 /*break*/, 4];
211
- case 4: return [2 /*return*/];
289
+ return [2 /*return*/];
212
290
  }
213
291
  });
214
292
  });
@@ -231,17 +309,28 @@ var Chirpier = /** @class */ (function () {
231
309
  };
232
310
  // Stop the timeout and uninitialize the Chirpier instance
233
311
  Chirpier.stop = function () {
234
- if (!Chirpier.instance) {
235
- return;
236
- }
237
- if (Chirpier.instance.flushTimeoutId) {
238
- clearTimeout(Chirpier.instance.flushTimeoutId);
239
- Chirpier.instance.flushTimeoutId = null;
240
- }
241
- // Flush any remaining events in the queue
242
- Chirpier.instance.flushQueue();
243
- // Uninitialize the Chirpier instance
244
- Chirpier.instance = null;
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
+ });
245
334
  };
246
335
  Chirpier.instance = null;
247
336
  return Chirpier;
@@ -294,10 +383,14 @@ function initialize(options) {
294
383
  }
295
384
  catch (error) {
296
385
  if (error instanceof ChirpierError) {
297
- 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
+ }
298
389
  }
299
390
  else {
300
- 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
+ }
301
394
  }
302
395
  throw error;
303
396
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chirpier/chirpier-js",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Chirpier SDK for JavaScript",
5
5
  "keywords": [
6
6
  "chirpier",
@@ -27,6 +27,7 @@
27
27
  "test": "jest --coverage"
28
28
  },
29
29
  "dependencies": {
30
+ "async-lock": "^1.4.1",
30
31
  "axios": "^0.24.0",
31
32
  "axios-retry": "^4.5.0",
32
33
  "js-base64": "^3.7.7",
@@ -36,6 +37,7 @@
36
37
  "devDependencies": {
37
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",
@@ -1,4 +1,4 @@
1
- import { Chirpier, ChirpierError, Event, initialize, monitor } from "../index";
1
+ import { Chirpier, ChirpierError, Event, LogLevel, initialize, monitor } from "../index";
2
2
  import {
3
3
  DEFAULT_API_ENDPOINT,
4
4
  DEFAULT_RETRIES,
@@ -13,7 +13,7 @@ describe("Chirpier SDK", () => {
13
13
  describe("Initialization", () => {
14
14
  test("should throw error if monitor is called before initialize", () => {
15
15
  const event: Event = {
16
- group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
16
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
17
17
  stream_name: "test-stream",
18
18
  value: 1,
19
19
  };
@@ -27,6 +27,7 @@ describe("Chirpier SDK", () => {
27
27
  test("should initialize with default values", () => {
28
28
  initialize({
29
29
  key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
30
+ logLevel: LogLevel.None
30
31
  });
31
32
  const chirpier = Chirpier.getInstance({} as any);
32
33
 
@@ -38,7 +39,7 @@ describe("Chirpier SDK", () => {
38
39
  expect(chirpier?.["retries"]).toBe(DEFAULT_RETRIES);
39
40
  expect(chirpier?.["timeout"]).toBe(DEFAULT_TIMEOUT);
40
41
  expect(chirpier?.["batchSize"]).toBe(DEFAULT_BATCH_SIZE);
41
- expect(chirpier?.["flushTimeout"]).toBe(DEFAULT_FLUSH_DELAY);
42
+ expect(chirpier?.["flushDelay"]).toBe(DEFAULT_FLUSH_DELAY);
42
43
 
43
44
  Chirpier.stop();
44
45
  });
@@ -47,11 +48,13 @@ describe("Chirpier SDK", () => {
47
48
  expect(() => {
48
49
  initialize({
49
50
  key: "api_key",
51
+ logLevel: LogLevel.None
50
52
  });
51
53
  }).toThrow(ChirpierError);
52
54
  expect(() => {
53
55
  initialize({
54
56
  key: "api_key",
57
+ logLevel: LogLevel.None
55
58
  });
56
59
  }).toThrow("Invalid API key: Not a valid JWT");
57
60
  });
@@ -65,10 +68,11 @@ describe("Chirpier SDK", () => {
65
68
 
66
69
  initialize({
67
70
  key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
71
+ logLevel: LogLevel.None
68
72
  });
69
73
 
70
74
  const event: Event = {
71
- group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
75
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
72
76
  stream_name: "test-stream",
73
77
  value: 1,
74
78
  };
@@ -81,7 +85,7 @@ describe("Chirpier SDK", () => {
81
85
  expect(mock.history.post[0].url).toBe(DEFAULT_API_ENDPOINT);
82
86
  expect(JSON.parse(mock.history.post[0].data)).toEqual([
83
87
  {
84
- group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
88
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
85
89
  stream_name: "test-stream",
86
90
  value: 1,
87
91
  },
@@ -95,11 +99,12 @@ describe("Chirpier SDK", () => {
95
99
  test("should throw error for invalid event", async () => {
96
100
  initialize({
97
101
  key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
102
+ logLevel: LogLevel.None
98
103
  });
99
104
  const chirpier = Chirpier.getInstance({} as any);
100
105
 
101
106
  const invalidEvent = {
102
- group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
107
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
103
108
  } as any;
104
109
  await expect(chirpier?.monitor(invalidEvent)).rejects.toThrow(
105
110
  ChirpierError
@@ -115,10 +120,11 @@ describe("Chirpier SDK", () => {
115
120
 
116
121
  initialize({
117
122
  key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
123
+ logLevel: LogLevel.None
118
124
  });
119
125
 
120
126
  const event: Event = {
121
- group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
127
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
122
128
  stream_name: "test-stream",
123
129
  value: 1,
124
130
  };
@@ -141,10 +147,11 @@ describe("Chirpier SDK", () => {
141
147
 
142
148
  initialize({
143
149
  key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
150
+ logLevel: LogLevel.None
144
151
  });
145
152
 
146
153
  const event: Event = {
147
- group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
154
+ group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
148
155
  stream_name: "test-stream",
149
156
  value: 1,
150
157
  };
package/src/constants.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  // constants.ts
2
2
 
3
3
  export const DEFAULT_API_ENDPOINT = 'https://events.chirpier.co/v1.0/events';
4
- export const DEFAULT_RETRIES = 30;
4
+ export const DEFAULT_RETRIES = 15;
5
5
  export const DEFAULT_TIMEOUT = 5000
6
- export const DEFAULT_BATCH_SIZE = 50;
7
- export const DEFAULT_FLUSH_DELAY = 500;
6
+ export const DEFAULT_BATCH_SIZE = 100;
7
+ export const DEFAULT_FLUSH_DELAY = 500;
8
+ export const MAX_QUEUE_SIZE = 2000;
package/src/index.ts CHANGED
@@ -8,11 +8,22 @@ import {
8
8
  DEFAULT_TIMEOUT,
9
9
  DEFAULT_BATCH_SIZE,
10
10
  DEFAULT_FLUSH_DELAY,
11
+ MAX_QUEUE_SIZE,
11
12
  } from "./constants";
13
+ import AsyncLock from "async-lock";
14
+
15
+ // Define logging levels
16
+ export enum LogLevel {
17
+ None = 0,
18
+ Error = 1,
19
+ Info = 2,
20
+ Debug = 3,
21
+ }
12
22
 
13
23
  // Define the options interface for Chirpier initialization
14
24
  interface Options {
15
25
  key: string;
26
+ logLevel?: LogLevel;
16
27
  }
17
28
 
18
29
  // Define the Event interface for monitoring
@@ -31,6 +42,12 @@ export class ChirpierError extends Error {
31
42
  }
32
43
  }
33
44
 
45
+ interface QueuedEvent {
46
+ event: Event;
47
+ timestamp: number;
48
+ retryCount: number;
49
+ }
50
+
34
51
  /**
35
52
  * Main Chirpier class for monitoring events.
36
53
  */
@@ -41,30 +58,32 @@ export class Chirpier {
41
58
  private readonly retries: number;
42
59
  private readonly timeout: number;
43
60
  private readonly axiosInstance: AxiosInstance;
44
- private eventQueue: Event[] = [];
61
+ private eventQueue: QueuedEvent[] = [];
45
62
  private readonly batchSize: number;
46
- private readonly flushTimeout: number;
63
+ private readonly flushDelay: number;
47
64
  private flushTimeoutId: NodeJS.Timeout | null = null;
65
+ private readonly queueLock = new AsyncLock();
66
+ private readonly flushLock = new AsyncLock();
67
+ private readonly logLevel: LogLevel;
48
68
 
49
69
  /**
50
70
  * Initializes a new instance of the Chirpier class.
51
71
  * @param options - Configuration options for the SDK.
52
72
  */
53
73
  private constructor(options: Options) {
54
- const {
55
- key,
56
- } = options;
74
+ const { key, logLevel = LogLevel.None } = options;
57
75
 
58
76
  if (!key || typeof key !== "string") {
59
77
  throw new ChirpierError("API key is required and must be a string");
60
78
  }
61
-
79
+
62
80
  this.apiKey = key;
63
81
  this.apiEndpoint = DEFAULT_API_ENDPOINT;
64
82
  this.retries = DEFAULT_RETRIES;
65
83
  this.timeout = DEFAULT_TIMEOUT;
66
84
  this.batchSize = DEFAULT_BATCH_SIZE;
67
- this.flushTimeout = DEFAULT_FLUSH_DELAY;
85
+ this.flushDelay = DEFAULT_FLUSH_DELAY;
86
+ this.logLevel = logLevel;
68
87
 
69
88
  // Create axios instance with authorization header
70
89
  this.axiosInstance = axios.create({
@@ -137,13 +156,21 @@ export class Chirpier {
137
156
  );
138
157
  }
139
158
 
140
- this.eventQueue.push(event);
159
+ await this.queueLock.acquire("queue", async () => {
160
+ if (this.eventQueue.length >= MAX_QUEUE_SIZE) {
161
+ throw new ChirpierError("Event queue is full.");
162
+ }
163
+
164
+ this.eventQueue.push({ event, timestamp: Date.now(), retryCount: 0 });
165
+ });
141
166
 
142
167
  if (this.eventQueue.length >= this.batchSize) {
143
- console.info(`Batch size reached. Flushing queue.`);
144
168
  await this.flushQueue();
145
169
  } else if (!this.flushTimeoutId) {
146
- this.flushTimeoutId = setTimeout(() => this.flushQueue(), this.flushTimeout);
170
+ this.flushTimeoutId = setTimeout(
171
+ () => this.flushQueue(),
172
+ this.flushDelay
173
+ );
147
174
  }
148
175
  }
149
176
 
@@ -151,24 +178,65 @@ export class Chirpier {
151
178
  * Flushes the event queue by sending all events to the API.
152
179
  */
153
180
  private async flushQueue(): Promise<void> {
154
- if (this.flushTimeoutId) {
155
- clearTimeout(this.flushTimeoutId);
156
- this.flushTimeoutId = null;
157
- }
181
+ // Acquire the flush lock
182
+ await this.flushLock.acquire("flush", async () => {
183
+ let eventsToSend: QueuedEvent[] = [];
158
184
 
159
- if (this.eventQueue.length === 0) {
160
- return;
161
- }
185
+ // Extract events from the queue under the queue lock
186
+ await this.queueLock.acquire("eventQueue", async () => {
187
+ if (this.eventQueue.length > 0) {
188
+ eventsToSend = [...this.eventQueue];
189
+ this.eventQueue = [];
190
+ }
191
+ });
162
192
 
163
- const eventsToSend = [...this.eventQueue];
164
- this.eventQueue = [];
193
+ if (eventsToSend.length === 0) {
194
+ return;
195
+ }
165
196
 
166
- try {
167
- await this.sendEvents(eventsToSend);
168
- console.info(`Successfully sent ${eventsToSend.length} events`);
169
- } catch (error) {
170
- console.error("Failed to send events:", error);
171
- }
197
+ try {
198
+ // Clear any pending flush timeout
199
+ if (this.flushTimeoutId) {
200
+ clearTimeout(this.flushTimeoutId);
201
+ this.flushTimeoutId = null;
202
+ }
203
+
204
+ // Attempt to send events
205
+ await this.sendEvents(eventsToSend.map((qe) => qe.event));
206
+
207
+ if (this.logLevel >= LogLevel.Info) {
208
+ console.info(`Successfully sent ${eventsToSend.length} events`);
209
+ }
210
+ } catch (error) {
211
+ // Log failure
212
+ if (this.logLevel >= LogLevel.Error) {
213
+ console.error("Failed to send events:", error);
214
+ }
215
+
216
+ // Requeue failed events with retry count checks
217
+ const retryableEvents: QueuedEvent[] = [];
218
+ for (const queuedEvent of eventsToSend) {
219
+ if (queuedEvent.retryCount >= this.retries) {
220
+ if (this.logLevel >= LogLevel.Error) {
221
+ console.error(
222
+ `Dropping event after ${this.retries} retries:`,
223
+ queuedEvent.event
224
+ );
225
+ }
226
+ continue; // Skip adding this event back to the queue
227
+ }
228
+
229
+ // Increment retry count and add back to the queue
230
+ queuedEvent.retryCount++;
231
+ retryableEvents.push(queuedEvent);
232
+ }
233
+
234
+ // Requeue remaining retryable events
235
+ await this.queueLock.acquire("eventQueue", async () => {
236
+ this.eventQueue = [...retryableEvents, ...this.eventQueue];
237
+ });
238
+ }
239
+ });
172
240
  }
173
241
 
174
242
  /**
@@ -178,9 +246,9 @@ export class Chirpier {
178
246
  private async sendEvents(events: Event[]): Promise<void> {
179
247
  await this.axiosInstance.post(this.apiEndpoint, events);
180
248
  }
181
-
249
+
182
250
  // Stop the timeout and uninitialize the Chirpier instance
183
- public static stop(): void {
251
+ public static async stop(): Promise<void> {
184
252
  if (!Chirpier.instance) {
185
253
  return;
186
254
  }
@@ -189,7 +257,7 @@ export class Chirpier {
189
257
  Chirpier.instance.flushTimeoutId = null;
190
258
  }
191
259
  // Flush any remaining events in the queue
192
- Chirpier.instance.flushQueue();
260
+ await Chirpier.instance.flushQueue();
193
261
  // Uninitialize the Chirpier instance
194
262
  Chirpier.instance = null;
195
263
  }
@@ -243,12 +311,16 @@ export function initialize(options: Options): void {
243
311
  Chirpier.getInstance(options);
244
312
  } catch (error) {
245
313
  if (error instanceof ChirpierError) {
246
- console.error("Failed to initialize Chirpier SDK:", error.message);
314
+ if (options.logLevel && options.logLevel >= LogLevel.Error) {
315
+ console.error("Failed to initialize Chirpier SDK:", error.message);
316
+ }
247
317
  } else {
248
- console.error(
249
- "An unexpected error occurred during Chirpier SDK initialization:",
250
- error
251
- );
318
+ if (options.logLevel && options.logLevel >= LogLevel.Error) {
319
+ console.error(
320
+ "An unexpected error occurred during Chirpier SDK initialization:",
321
+ error
322
+ );
323
+ }
252
324
  }
253
325
  throw error;
254
326
  }
@@ -265,7 +337,7 @@ export function monitor(event: Event): void {
265
337
  "Chirpier SDK is not initialized. Please call initialize() first."
266
338
  );
267
339
  }
268
-
340
+
269
341
  instance.monitor(event).catch((error) => {
270
342
  console.error("Error in monitor function:", error);
271
343
  });