@chirpier/chirpier-js 0.1.1 → 0.1.2
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/__tests__/sdk.test.d.ts +0 -3
- package/dist/__tests__/sdk.test.d.ts.map +1 -1
- package/dist/__tests__/sdk.test.js +120 -11
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +63 -28
- package/package.json +2 -3
- package/src/__tests__/sdk.test.ts +116 -12
- package/src/index.ts +59 -14
- package/src/storage.ts +0 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/sdk.test.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sdk.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/sdk.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* @jest-environment jsdom
|
|
4
|
-
*/
|
|
5
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
6
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
7
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -47,6 +44,8 @@ var constants_1 = require("../constants");
|
|
|
47
44
|
var axios_mock_adapter_1 = __importDefault(require("axios-mock-adapter"));
|
|
48
45
|
var axios_1 = __importDefault(require("axios"));
|
|
49
46
|
var server_1 = require("./mocks/server");
|
|
47
|
+
var uuid_1 = require("@lukeed/uuid");
|
|
48
|
+
jest.mock("@lukeed/uuid");
|
|
50
49
|
describe("Chirpier SDK", function () {
|
|
51
50
|
var chirpier;
|
|
52
51
|
afterEach(function () {
|
|
@@ -88,10 +87,17 @@ describe("Chirpier SDK", function () {
|
|
|
88
87
|
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
89
88
|
expect(function () { return new index_1.Chirpier({}); }).toThrow(index_1.ChirpierError);
|
|
90
89
|
});
|
|
90
|
+
test("should throw error if key is not a valid JWT", function () {
|
|
91
|
+
expect(function () { return (0, index_1.initialize)({ key: "invalid_key" }); }).toThrow(index_1.ChirpierError);
|
|
92
|
+
});
|
|
93
|
+
test("should initialize successfully with a valid JWT", function () {
|
|
94
|
+
var validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
95
|
+
expect(function () { return (0, index_1.initialize)({ key: validJWT }); }).not.toThrow();
|
|
96
|
+
});
|
|
91
97
|
});
|
|
92
98
|
describe("monitor", function () {
|
|
93
99
|
test("event should be sent", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
94
|
-
var mock, event;
|
|
100
|
+
var mock, validJWT, event;
|
|
95
101
|
return __generator(this, function (_a) {
|
|
96
102
|
switch (_a.label) {
|
|
97
103
|
case 0:
|
|
@@ -100,6 +106,8 @@ describe("Chirpier SDK", function () {
|
|
|
100
106
|
});
|
|
101
107
|
mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
102
108
|
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
109
|
+
validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
110
|
+
(0, index_1.initialize)({ key: validJWT });
|
|
103
111
|
event = {
|
|
104
112
|
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
105
113
|
stream: "test-stream",
|
|
@@ -108,7 +116,9 @@ describe("Chirpier SDK", function () {
|
|
|
108
116
|
return [4 /*yield*/, chirpier.monitor(event)];
|
|
109
117
|
case 1:
|
|
110
118
|
_a.sent();
|
|
111
|
-
|
|
119
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 1000); })];
|
|
120
|
+
case 2:
|
|
121
|
+
_a.sent(); // Wait for flush
|
|
112
122
|
expect(mock.history.post.length).toBe(1);
|
|
113
123
|
expect(mock.history.post[0].url).toBe(constants_1.DEFAULT_API_ENDPOINT);
|
|
114
124
|
expect(JSON.parse(mock.history.post[0].data)).toEqual([
|
|
@@ -116,7 +126,6 @@ describe("Chirpier SDK", function () {
|
|
|
116
126
|
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
117
127
|
stream: "test-stream",
|
|
118
128
|
value: 1,
|
|
119
|
-
event_id: expect.any(String),
|
|
120
129
|
},
|
|
121
130
|
]);
|
|
122
131
|
// Clean up the mock
|
|
@@ -125,11 +134,8 @@ describe("Chirpier SDK", function () {
|
|
|
125
134
|
}
|
|
126
135
|
});
|
|
127
136
|
}); });
|
|
128
|
-
// Setup mock server
|
|
129
|
-
var mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
130
|
-
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
131
137
|
test("should throw error for invalid event", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
132
|
-
var invalidEvent;
|
|
138
|
+
var invalidEvent, mock;
|
|
133
139
|
return __generator(this, function (_a) {
|
|
134
140
|
switch (_a.label) {
|
|
135
141
|
case 0:
|
|
@@ -142,7 +148,110 @@ describe("Chirpier SDK", function () {
|
|
|
142
148
|
return [4 /*yield*/, expect(chirpier.monitor(invalidEvent)).rejects.toThrow(index_1.ChirpierError)];
|
|
143
149
|
case 1:
|
|
144
150
|
_a.sent();
|
|
145
|
-
|
|
151
|
+
mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
152
|
+
mock.reset();
|
|
153
|
+
return [2 /*return*/];
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}); });
|
|
157
|
+
test("should batch events and flush when batch size is reached", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
158
|
+
var mock, validJWT, event;
|
|
159
|
+
return __generator(this, function (_a) {
|
|
160
|
+
switch (_a.label) {
|
|
161
|
+
case 0:
|
|
162
|
+
mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
163
|
+
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
164
|
+
validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
165
|
+
(0, index_1.initialize)({ key: validJWT, batchSize: 2 });
|
|
166
|
+
event = {
|
|
167
|
+
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
168
|
+
stream: "test-stream",
|
|
169
|
+
value: 1,
|
|
170
|
+
};
|
|
171
|
+
(0, index_1.monitor)(event);
|
|
172
|
+
(0, index_1.monitor)(event);
|
|
173
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 100); })];
|
|
174
|
+
case 1:
|
|
175
|
+
_a.sent(); // Wait for flush
|
|
176
|
+
expect(mock.history.post.length).toBe(1);
|
|
177
|
+
expect(JSON.parse(mock.history.post[0].data).length).toBe(2);
|
|
178
|
+
mock.reset();
|
|
179
|
+
return [2 /*return*/];
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}); });
|
|
183
|
+
test("should flush events after interval", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
184
|
+
var mock, validJWT, event;
|
|
185
|
+
return __generator(this, function (_a) {
|
|
186
|
+
switch (_a.label) {
|
|
187
|
+
case 0:
|
|
188
|
+
mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
189
|
+
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
190
|
+
validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
191
|
+
(0, index_1.initialize)({ key: validJWT, flushInterval: 100 });
|
|
192
|
+
event = {
|
|
193
|
+
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
194
|
+
stream: "test-stream",
|
|
195
|
+
value: 1,
|
|
196
|
+
};
|
|
197
|
+
(0, index_1.monitor)(event);
|
|
198
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 1000); })];
|
|
199
|
+
case 1:
|
|
200
|
+
_a.sent(); // Wait for flush
|
|
201
|
+
expect(mock.history.post.length).toBe(1);
|
|
202
|
+
expect(JSON.parse(mock.history.post[0].data).length).toBe(1);
|
|
203
|
+
mock.reset();
|
|
204
|
+
return [2 /*return*/];
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}); });
|
|
208
|
+
test("should use provided event_id if available", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
209
|
+
var mock, validJWT, event;
|
|
210
|
+
return __generator(this, function (_a) {
|
|
211
|
+
switch (_a.label) {
|
|
212
|
+
case 0:
|
|
213
|
+
mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
214
|
+
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
215
|
+
validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
216
|
+
(0, index_1.initialize)({ key: validJWT });
|
|
217
|
+
event = {
|
|
218
|
+
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
219
|
+
stream: "test-stream",
|
|
220
|
+
value: 1,
|
|
221
|
+
event_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
222
|
+
};
|
|
223
|
+
(0, index_1.monitor)(event);
|
|
224
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 1000); })];
|
|
225
|
+
case 1:
|
|
226
|
+
_a.sent(); // Wait for flush
|
|
227
|
+
expect(mock.history.post.length).toBe(1);
|
|
228
|
+
expect(JSON.parse(mock.history.post[0].data)[0].event_id).toBe("f3438ee9-b964-48aa-b938-a803df440a3c");
|
|
229
|
+
mock.reset();
|
|
230
|
+
return [2 /*return*/];
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}); });
|
|
234
|
+
test("should generate event_id if not provided", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
235
|
+
var mock, validJWT, event;
|
|
236
|
+
return __generator(this, function (_a) {
|
|
237
|
+
switch (_a.label) {
|
|
238
|
+
case 0:
|
|
239
|
+
mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
240
|
+
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
241
|
+
uuid_1.v4.mockReturnValue("generated-uuid");
|
|
242
|
+
validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
243
|
+
(0, index_1.initialize)({ key: validJWT });
|
|
244
|
+
event = {
|
|
245
|
+
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
246
|
+
stream: "test-stream",
|
|
247
|
+
value: 1,
|
|
248
|
+
};
|
|
249
|
+
(0, index_1.monitor)(event);
|
|
250
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 1000); })];
|
|
251
|
+
case 1:
|
|
252
|
+
_a.sent(); // Wait for flush
|
|
253
|
+
expect(mock.history.post.length).toBe(1);
|
|
254
|
+
expect(JSON.parse(mock.history.post[0].data)[0].event_id).toBe("generated-uuid");
|
|
146
255
|
mock.reset();
|
|
147
256
|
return [2 /*return*/];
|
|
148
257
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ interface Options {
|
|
|
3
3
|
apiEndpoint?: string;
|
|
4
4
|
retries?: number;
|
|
5
5
|
timeout?: number;
|
|
6
|
+
batchSize?: number;
|
|
7
|
+
flushInterval?: number;
|
|
6
8
|
}
|
|
7
9
|
export interface Event {
|
|
8
10
|
group_id: string;
|
|
@@ -22,11 +24,15 @@ export declare class Chirpier {
|
|
|
22
24
|
private readonly retries;
|
|
23
25
|
private readonly timeout;
|
|
24
26
|
private readonly axiosInstance;
|
|
27
|
+
private eventQueue;
|
|
28
|
+
private flushTimeout;
|
|
29
|
+
private readonly batchSize;
|
|
30
|
+
private readonly flushInterval;
|
|
25
31
|
/**
|
|
26
32
|
* Initializes a new instance of the Chirpier class.
|
|
27
33
|
* @param options - Configuration options for the SDK.
|
|
28
34
|
*/
|
|
29
|
-
constructor({ key, apiEndpoint, retries, timeout, }: Options);
|
|
35
|
+
constructor({ key, apiEndpoint, retries, timeout, batchSize, flushInterval, }: Options);
|
|
30
36
|
/**
|
|
31
37
|
* Validates the event structure.
|
|
32
38
|
* @param event - The event to validate.
|
|
@@ -34,15 +40,14 @@ export declare class Chirpier {
|
|
|
34
40
|
*/
|
|
35
41
|
private isValidEvent;
|
|
36
42
|
/**
|
|
37
|
-
* Monitors an event by
|
|
43
|
+
* Monitors an event by adding it to the queue and scheduling a flush if necessary.
|
|
38
44
|
* @param event - The event to monitor.
|
|
39
45
|
*/
|
|
40
46
|
monitor(event: Event): Promise<void>;
|
|
41
47
|
/**
|
|
42
|
-
*
|
|
43
|
-
* @param event - The event to send.
|
|
48
|
+
* Flushes the event queue by sending all events to the API.
|
|
44
49
|
*/
|
|
45
|
-
private
|
|
50
|
+
private flushQueue;
|
|
46
51
|
/**
|
|
47
52
|
* Sends multiple events to the API in a batch.
|
|
48
53
|
* @param events - The array of events to send.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/index.js
CHANGED
|
@@ -61,6 +61,15 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
61
61
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
62
62
|
}
|
|
63
63
|
};
|
|
64
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
65
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
66
|
+
if (ar || !(i in from)) {
|
|
67
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
68
|
+
ar[i] = from[i];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
72
|
+
};
|
|
64
73
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
65
74
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
66
75
|
};
|
|
@@ -93,7 +102,9 @@ var Chirpier = /** @class */ (function () {
|
|
|
93
102
|
* @param options - Configuration options for the SDK.
|
|
94
103
|
*/
|
|
95
104
|
function Chirpier(_a) {
|
|
96
|
-
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;
|
|
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;
|
|
106
|
+
this.eventQueue = [];
|
|
107
|
+
this.flushTimeout = null;
|
|
97
108
|
if (!key || typeof key !== "string") {
|
|
98
109
|
throw new ChirpierError("API key is required and must be a string");
|
|
99
110
|
}
|
|
@@ -101,6 +112,8 @@ var Chirpier = /** @class */ (function () {
|
|
|
101
112
|
this.apiEndpoint = apiEndpoint;
|
|
102
113
|
this.retries = retries;
|
|
103
114
|
this.timeout = timeout;
|
|
115
|
+
this.batchSize = batchSize;
|
|
116
|
+
this.flushInterval = flushInterval;
|
|
104
117
|
// Create axios instance with authorization header
|
|
105
118
|
this.axiosInstance = axios_1.default.create({
|
|
106
119
|
headers: { Authorization: "Bearer ".concat(this.apiKey) },
|
|
@@ -114,8 +127,8 @@ var Chirpier = /** @class */ (function () {
|
|
|
114
127
|
// Apply axios-retry to your Axios instance
|
|
115
128
|
(0, axios_retry_1.default)(this.axiosInstance, {
|
|
116
129
|
retries: this.retries,
|
|
117
|
-
retryDelay: function (retryCount
|
|
118
|
-
return Math.pow(2, retryCount) *
|
|
130
|
+
retryDelay: function (retryCount) {
|
|
131
|
+
return Math.pow(2, retryCount) * 1000; // Exponential backoff starting at 1 second
|
|
119
132
|
},
|
|
120
133
|
retryCondition: function (error) {
|
|
121
134
|
return (axios_retry_1.default.isNetworkError(error) || axios_retry_1.default.isRetryableError(error));
|
|
@@ -129,54 +142,76 @@ var Chirpier = /** @class */ (function () {
|
|
|
129
142
|
* @returns True if valid, false otherwise.
|
|
130
143
|
*/
|
|
131
144
|
Chirpier.prototype.isValidEvent = function (event) {
|
|
132
|
-
return (typeof event.group_id === "string" &&
|
|
145
|
+
return (typeof event.group_id === "string" &&
|
|
146
|
+
/^[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) &&
|
|
133
147
|
event.group_id.trim().length > 0 &&
|
|
134
148
|
typeof event.stream === "string" &&
|
|
135
149
|
event.stream.trim().length > 0 &&
|
|
136
150
|
typeof event.value === "number");
|
|
137
151
|
};
|
|
138
152
|
/**
|
|
139
|
-
* Monitors an event by
|
|
153
|
+
* Monitors an event by adding it to the queue and scheduling a flush if necessary.
|
|
140
154
|
* @param event - The event to monitor.
|
|
141
155
|
*/
|
|
142
156
|
Chirpier.prototype.monitor = function (event) {
|
|
143
157
|
return __awaiter(this, void 0, void 0, function () {
|
|
144
|
-
var eventWithID
|
|
158
|
+
var eventWithID;
|
|
159
|
+
var _this = this;
|
|
160
|
+
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);
|
|
174
|
+
}
|
|
175
|
+
return [2 /*return*/];
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Flushes the event queue by sending all events to the API.
|
|
181
|
+
*/
|
|
182
|
+
Chirpier.prototype.flushQueue = function () {
|
|
183
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
184
|
+
var eventsToSend, error_1;
|
|
185
|
+
var _this = this;
|
|
145
186
|
return __generator(this, function (_a) {
|
|
146
187
|
switch (_a.label) {
|
|
147
188
|
case 0:
|
|
148
|
-
if (
|
|
149
|
-
|
|
189
|
+
if (this.eventQueue.length === 0) {
|
|
190
|
+
return [2 /*return*/];
|
|
150
191
|
}
|
|
151
|
-
|
|
192
|
+
if (this.flushTimeout) {
|
|
193
|
+
clearTimeout(this.flushTimeout);
|
|
194
|
+
this.flushTimeout = null;
|
|
195
|
+
}
|
|
196
|
+
eventsToSend = __spreadArray([], this.eventQueue, true);
|
|
197
|
+
this.eventQueue = [];
|
|
152
198
|
_a.label = 1;
|
|
153
199
|
case 1:
|
|
154
200
|
_a.trys.push([1, 3, , 4]);
|
|
155
|
-
return [4 /*yield*/, this.
|
|
201
|
+
return [4 /*yield*/, this.sendEvents(eventsToSend)];
|
|
156
202
|
case 2:
|
|
157
203
|
_a.sent();
|
|
158
|
-
console.info("
|
|
204
|
+
console.info("Successfully sent ".concat(eventsToSend.length, " events"));
|
|
159
205
|
return [3 /*break*/, 4];
|
|
160
206
|
case 3:
|
|
161
207
|
error_1 = _a.sent();
|
|
162
|
-
console.error("Failed to send
|
|
208
|
+
console.error("Failed to send events:", error_1);
|
|
163
209
|
return [3 /*break*/, 4];
|
|
164
|
-
case 4:
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Sends an event to the API.
|
|
171
|
-
* @param event - The event to send.
|
|
172
|
-
*/
|
|
173
|
-
Chirpier.prototype.sendEvent = function (event) {
|
|
174
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
175
|
-
return __generator(this, function (_a) {
|
|
176
|
-
switch (_a.label) {
|
|
177
|
-
case 0: return [4 /*yield*/, this.sendEvents([event])];
|
|
178
|
-
case 1:
|
|
179
|
-
_a.sent();
|
|
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
|
+
}
|
|
180
215
|
return [2 /*return*/];
|
|
181
216
|
}
|
|
182
217
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chirpier/chirpier-js",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Chirpier SDK for JavaScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chirpier",
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
"@lukeed/uuid": "^2.0.1",
|
|
31
31
|
"axios": "^0.24.0",
|
|
32
32
|
"axios-retry": "^4.5.0",
|
|
33
|
-
"jest-environment-jsdom": "^29.7.0",
|
|
34
33
|
"js-base64": "^3.7.7",
|
|
35
34
|
"ts-node": "^10.9.2",
|
|
36
35
|
"tslib": "^2.3.0"
|
|
@@ -39,7 +38,7 @@
|
|
|
39
38
|
"@jest/globals": "^29.7.0",
|
|
40
39
|
"@types/jest": "^29.5.13",
|
|
41
40
|
"@types/mocha": "^10.0.8",
|
|
42
|
-
"@types/node": "^22.5
|
|
41
|
+
"@types/node": "^22.7.5",
|
|
43
42
|
"@types/uuid": "^10.0.0",
|
|
44
43
|
"axios-mock-adapter": "^2.0.0",
|
|
45
44
|
"jest": "^29.7.0",
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
* @jest-environment jsdom
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { Chirpier, ChirpierError, Event } from "../index";
|
|
1
|
+
import { Chirpier, ChirpierError, Event, initialize, monitor } from "../index";
|
|
6
2
|
import { DEFAULT_API_ENDPOINT, DEFAULT_RETRIES } from "../constants";
|
|
7
3
|
import MockAdapter from "axios-mock-adapter";
|
|
8
4
|
import axios from "axios";
|
|
9
5
|
import { cleanupMockServer } from "./mocks/server";
|
|
6
|
+
import { v4 as uuidv4 } from "@lukeed/uuid";
|
|
7
|
+
|
|
8
|
+
jest.mock("@lukeed/uuid");
|
|
10
9
|
|
|
11
10
|
describe("Chirpier SDK", () => {
|
|
12
11
|
let chirpier: Chirpier;
|
|
@@ -60,6 +59,15 @@ describe("Chirpier SDK", () => {
|
|
|
60
59
|
|
|
61
60
|
expect(() => new Chirpier({} as any)).toThrow(ChirpierError);
|
|
62
61
|
});
|
|
62
|
+
|
|
63
|
+
test("should throw error if key is not a valid JWT", () => {
|
|
64
|
+
expect(() => initialize({ key: "invalid_key" })).toThrow(ChirpierError);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("should initialize successfully with a valid JWT", () => {
|
|
68
|
+
const validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
69
|
+
expect(() => initialize({ key: validJWT })).not.toThrow();
|
|
70
|
+
});
|
|
63
71
|
});
|
|
64
72
|
|
|
65
73
|
describe("monitor", () => {
|
|
@@ -72,6 +80,9 @@ describe("Chirpier SDK", () => {
|
|
|
72
80
|
const mock = new MockAdapter(axios);
|
|
73
81
|
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
74
82
|
|
|
83
|
+
const validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
84
|
+
initialize({ key: validJWT });
|
|
85
|
+
|
|
75
86
|
const event: Event = {
|
|
76
87
|
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
77
88
|
stream: "test-stream",
|
|
@@ -80,7 +91,8 @@ describe("Chirpier SDK", () => {
|
|
|
80
91
|
|
|
81
92
|
await chirpier.monitor(event);
|
|
82
93
|
|
|
83
|
-
|
|
94
|
+
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for flush
|
|
95
|
+
|
|
84
96
|
expect(mock.history.post.length).toBe(1);
|
|
85
97
|
expect(mock.history.post[0].url).toBe(DEFAULT_API_ENDPOINT);
|
|
86
98
|
expect(JSON.parse(mock.history.post[0].data)).toEqual([
|
|
@@ -88,7 +100,6 @@ describe("Chirpier SDK", () => {
|
|
|
88
100
|
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
89
101
|
stream: "test-stream",
|
|
90
102
|
value: 1,
|
|
91
|
-
event_id: expect.any(String),
|
|
92
103
|
},
|
|
93
104
|
]);
|
|
94
105
|
|
|
@@ -96,10 +107,6 @@ describe("Chirpier SDK", () => {
|
|
|
96
107
|
mock.reset();
|
|
97
108
|
});
|
|
98
109
|
|
|
99
|
-
// Setup mock server
|
|
100
|
-
const mock = new MockAdapter(axios);
|
|
101
|
-
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
102
|
-
|
|
103
110
|
test("should throw error for invalid event", async () => {
|
|
104
111
|
chirpier = new Chirpier({
|
|
105
112
|
key: "api_key",
|
|
@@ -112,7 +119,104 @@ describe("Chirpier SDK", () => {
|
|
|
112
119
|
);
|
|
113
120
|
|
|
114
121
|
// Clean up the mock
|
|
122
|
+
const mock = new MockAdapter(axios);
|
|
123
|
+
mock.reset();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("should batch events and flush when batch size is reached", async () => {
|
|
127
|
+
const mock = new MockAdapter(axios);
|
|
128
|
+
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
129
|
+
|
|
130
|
+
const validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
131
|
+
initialize({ key: validJWT, batchSize: 2 });
|
|
132
|
+
|
|
133
|
+
const event: Event = {
|
|
134
|
+
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
135
|
+
stream: "test-stream",
|
|
136
|
+
value: 1,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
monitor(event);
|
|
140
|
+
monitor(event);
|
|
141
|
+
|
|
142
|
+
await new Promise(resolve => setTimeout(resolve, 100)); // Wait for flush
|
|
143
|
+
|
|
144
|
+
expect(mock.history.post.length).toBe(1);
|
|
145
|
+
expect(JSON.parse(mock.history.post[0].data).length).toBe(2);
|
|
146
|
+
|
|
147
|
+
mock.reset();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("should flush events after interval", async () => {
|
|
151
|
+
const mock = new MockAdapter(axios);
|
|
152
|
+
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
153
|
+
|
|
154
|
+
const validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
155
|
+
initialize({ key: validJWT, flushInterval: 100 });
|
|
156
|
+
|
|
157
|
+
const event: Event = {
|
|
158
|
+
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
159
|
+
stream: "test-stream",
|
|
160
|
+
value: 1,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
monitor(event);
|
|
164
|
+
|
|
165
|
+
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for flush
|
|
166
|
+
|
|
167
|
+
expect(mock.history.post.length).toBe(1);
|
|
168
|
+
expect(JSON.parse(mock.history.post[0].data).length).toBe(1);
|
|
169
|
+
|
|
170
|
+
mock.reset();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("should use provided event_id if available", async () => {
|
|
174
|
+
const mock = new MockAdapter(axios);
|
|
175
|
+
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
176
|
+
|
|
177
|
+
const validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
178
|
+
initialize({ key: validJWT });
|
|
179
|
+
|
|
180
|
+
const event: Event = {
|
|
181
|
+
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
182
|
+
stream: "test-stream",
|
|
183
|
+
value: 1,
|
|
184
|
+
event_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
monitor(event);
|
|
188
|
+
|
|
189
|
+
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for flush
|
|
190
|
+
|
|
191
|
+
expect(mock.history.post.length).toBe(1);
|
|
192
|
+
expect(JSON.parse(mock.history.post[0].data)[0].event_id).toBe("f3438ee9-b964-48aa-b938-a803df440a3c");
|
|
193
|
+
|
|
194
|
+
mock.reset();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("should generate event_id if not provided", async () => {
|
|
198
|
+
const mock = new MockAdapter(axios);
|
|
199
|
+
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
200
|
+
|
|
201
|
+
(uuidv4 as jest.Mock).mockReturnValue("generated-uuid");
|
|
202
|
+
|
|
203
|
+
const validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
204
|
+
initialize({ key: validJWT });
|
|
205
|
+
|
|
206
|
+
const event: Event = {
|
|
207
|
+
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
208
|
+
stream: "test-stream",
|
|
209
|
+
value: 1,
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
monitor(event);
|
|
213
|
+
|
|
214
|
+
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for flush
|
|
215
|
+
|
|
216
|
+
expect(mock.history.post.length).toBe(1);
|
|
217
|
+
expect(JSON.parse(mock.history.post[0].data)[0].event_id).toBe("generated-uuid");
|
|
218
|
+
|
|
115
219
|
mock.reset();
|
|
116
220
|
});
|
|
117
221
|
});
|
|
118
|
-
});
|
|
222
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import axios, { AxiosInstance } from "axios";
|
|
3
3
|
import axiosRetry from "axios-retry";
|
|
4
4
|
import { v4 as uuidv4 } from "@lukeed/uuid";
|
|
5
|
-
import { Base64 } from
|
|
5
|
+
import { Base64 } from "js-base64";
|
|
6
6
|
import {
|
|
7
7
|
DEFAULT_API_ENDPOINT,
|
|
8
8
|
DEFAULT_RETRIES,
|
|
@@ -15,6 +15,8 @@ interface Options {
|
|
|
15
15
|
apiEndpoint?: string;
|
|
16
16
|
retries?: number;
|
|
17
17
|
timeout?: number;
|
|
18
|
+
batchSize?: number;
|
|
19
|
+
flushInterval?: number;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
// Define the Event interface for monitoring
|
|
@@ -43,6 +45,10 @@ export class Chirpier {
|
|
|
43
45
|
private readonly retries: number;
|
|
44
46
|
private readonly timeout: number;
|
|
45
47
|
private readonly axiosInstance: AxiosInstance;
|
|
48
|
+
private eventQueue: Event[] = [];
|
|
49
|
+
private flushTimeout: NodeJS.Timeout | null = null;
|
|
50
|
+
private readonly batchSize: number;
|
|
51
|
+
private readonly flushInterval: number;
|
|
46
52
|
|
|
47
53
|
/**
|
|
48
54
|
* Initializes a new instance of the Chirpier class.
|
|
@@ -53,6 +59,8 @@ export class Chirpier {
|
|
|
53
59
|
apiEndpoint = DEFAULT_API_ENDPOINT,
|
|
54
60
|
retries = DEFAULT_RETRIES,
|
|
55
61
|
timeout = DEFAULT_TIMEOUT,
|
|
62
|
+
batchSize = 100,
|
|
63
|
+
flushInterval = 500,
|
|
56
64
|
}: Options) {
|
|
57
65
|
if (!key || typeof key !== "string") {
|
|
58
66
|
throw new ChirpierError("API key is required and must be a string");
|
|
@@ -61,6 +69,8 @@ export class Chirpier {
|
|
|
61
69
|
this.apiEndpoint = apiEndpoint;
|
|
62
70
|
this.retries = retries;
|
|
63
71
|
this.timeout = timeout;
|
|
72
|
+
this.batchSize = batchSize;
|
|
73
|
+
this.flushInterval = flushInterval;
|
|
64
74
|
|
|
65
75
|
// Create axios instance with authorization header
|
|
66
76
|
this.axiosInstance = axios.create({
|
|
@@ -80,8 +90,8 @@ export class Chirpier {
|
|
|
80
90
|
// Apply axios-retry to your Axios instance
|
|
81
91
|
axiosRetry(this.axiosInstance, {
|
|
82
92
|
retries: this.retries,
|
|
83
|
-
retryDelay: (retryCount
|
|
84
|
-
return Math.pow(2, retryCount) *
|
|
93
|
+
retryDelay: (retryCount) => {
|
|
94
|
+
return Math.pow(2, retryCount) * 1000; // Exponential backoff starting at 1 second
|
|
85
95
|
},
|
|
86
96
|
retryCondition: (error) => {
|
|
87
97
|
return (
|
|
@@ -99,7 +109,10 @@ export class Chirpier {
|
|
|
99
109
|
*/
|
|
100
110
|
private isValidEvent(event: Event): boolean {
|
|
101
111
|
return (
|
|
102
|
-
typeof event.group_id === "string" &&
|
|
112
|
+
typeof event.group_id === "string" &&
|
|
113
|
+
/^[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(
|
|
114
|
+
event.group_id
|
|
115
|
+
) &&
|
|
103
116
|
event.group_id.trim().length > 0 &&
|
|
104
117
|
typeof event.stream === "string" &&
|
|
105
118
|
event.stream.trim().length > 0 &&
|
|
@@ -108,10 +121,14 @@ export class Chirpier {
|
|
|
108
121
|
}
|
|
109
122
|
|
|
110
123
|
/**
|
|
111
|
-
* Monitors an event by
|
|
124
|
+
* Monitors an event by adding it to the queue and scheduling a flush if necessary.
|
|
112
125
|
* @param event - The event to monitor.
|
|
113
126
|
*/
|
|
114
127
|
public async monitor(event: Event): Promise<void> {
|
|
128
|
+
if (!this.apiKey) {
|
|
129
|
+
throw new ChirpierError("Chirpier SDK must be initialized before calling monitor()");
|
|
130
|
+
}
|
|
131
|
+
|
|
115
132
|
if (!this.isValidEvent(event)) {
|
|
116
133
|
throw new ChirpierError(
|
|
117
134
|
"Invalid event format. Must include group_id, stream, and numeric value."
|
|
@@ -121,20 +138,48 @@ export class Chirpier {
|
|
|
121
138
|
// Ensure event_id is only set once
|
|
122
139
|
const eventWithID = { ...event, event_id: event.event_id || uuidv4() };
|
|
123
140
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
141
|
+
this.eventQueue.push(eventWithID);
|
|
142
|
+
|
|
143
|
+
if (this.eventQueue.length >= this.batchSize) {
|
|
144
|
+
this.flushQueue();
|
|
145
|
+
} else if (!this.flushTimeout) {
|
|
146
|
+
this.flushTimeout = setTimeout(
|
|
147
|
+
() => this.flushQueue(),
|
|
148
|
+
this.flushInterval
|
|
149
|
+
);
|
|
129
150
|
}
|
|
130
151
|
}
|
|
131
152
|
|
|
132
153
|
/**
|
|
133
|
-
*
|
|
134
|
-
* @param event - The event to send.
|
|
154
|
+
* Flushes the event queue by sending all events to the API.
|
|
135
155
|
*/
|
|
136
|
-
private async
|
|
137
|
-
|
|
156
|
+
private async flushQueue(): Promise<void> {
|
|
157
|
+
if (this.eventQueue.length === 0) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (this.flushTimeout) {
|
|
162
|
+
clearTimeout(this.flushTimeout);
|
|
163
|
+
this.flushTimeout = null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const eventsToSend = [...this.eventQueue];
|
|
167
|
+
this.eventQueue = [];
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
await this.sendEvents(eventsToSend);
|
|
171
|
+
console.info(`Successfully sent ${eventsToSend.length} events`);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.error("Failed to send events:", error);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Schedule next flush if there are more events
|
|
177
|
+
if (this.eventQueue.length > 0) {
|
|
178
|
+
this.flushTimeout = setTimeout(
|
|
179
|
+
() => this.flushQueue(),
|
|
180
|
+
this.flushInterval
|
|
181
|
+
);
|
|
182
|
+
}
|
|
138
183
|
}
|
|
139
184
|
|
|
140
185
|
/**
|
package/src/storage.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export class LocalStorageStorage {
|
|
2
|
-
save(events: any[]): void {
|
|
3
|
-
localStorage.setItem('chirpier_events', JSON.stringify(events));
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
load(): any[] {
|
|
7
|
-
const storedEvents = localStorage.getItem('chirpier_events');
|
|
8
|
-
return storedEvents ? JSON.parse(storedEvents) : [];
|
|
9
|
-
}
|
|
10
|
-
}
|