@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/README.md +15 -20
- package/dist/__tests__/chirpier.test.d.ts +2 -0
- package/dist/__tests__/chirpier.test.d.ts.map +1 -0
- package/dist/__tests__/chirpier.test.js +205 -0
- package/dist/__tests__/sdk.test.js +58 -121
- package/dist/constants.d.ts +5 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +6 -3
- package/dist/index.d.ts +22 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +174 -71
- package/package.json +7 -2
- package/src/__tests__/chirpier.test.ts +170 -0
- package/src/constants.ts +6 -3
- package/src/index.ts +147 -72
- package/src/__tests__/mocks/server.ts +0 -6
- package/src/__tests__/sdk.test.ts +0 -222
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
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(
|
|
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.
|
|
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 =
|
|
113
|
-
this.retries =
|
|
114
|
-
this.timeout =
|
|
115
|
-
this.batchSize =
|
|
116
|
-
this.
|
|
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.
|
|
149
|
-
event.
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
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
|
-
|
|
382
|
+
Chirpier.getInstance(options);
|
|
285
383
|
}
|
|
286
384
|
catch (error) {
|
|
287
385
|
if (error instanceof ChirpierError) {
|
|
288
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
"
|
|
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/
|
|
4
|
-
export const DEFAULT_RETRIES =
|
|
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;
|