@chirpier/chirpier-js 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -9
- package/dist/__tests__/sdk.test.js +58 -121
- package/dist/constants.d.ts +3 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +4 -2
- package/dist/index.d.ts +12 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +63 -53
- package/package.json +5 -2
- package/src/__tests__/sdk.test.ts +65 -124
- package/src/constants.ts +4 -2
- package/src/index.ts +62 -59
- package/src/__tests__/mocks/server.ts +0 -6
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ The Chirpier SDK is a lightweight, versatile library for monitoring and tracking
|
|
|
13
13
|
|
|
14
14
|
You can install the Chirpier SDK via npm:
|
|
15
15
|
```
|
|
16
|
-
npm install @chirpier/
|
|
16
|
+
npm install @chirpier/chirpier-js
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## Getting Started
|
|
@@ -24,7 +24,7 @@ To start using the SDK, you need to initialize it with your API key. The SDK wor
|
|
|
24
24
|
|
|
25
25
|
#### In a Browser
|
|
26
26
|
```
|
|
27
|
-
import { initialize, monitor } from '@chirpier/
|
|
27
|
+
import { initialize, monitor } from '@chirpier/chirpier-js';
|
|
28
28
|
|
|
29
29
|
// Initialize the SDK with your API key
|
|
30
30
|
initialize({ key: 'your-api-key' });
|
|
@@ -32,7 +32,7 @@ initialize({ key: 'your-api-key' });
|
|
|
32
32
|
// Send a data stream tied to a group of streams
|
|
33
33
|
monitor({
|
|
34
34
|
group_id: '02e4f4d8-415e-4fc1-b01a-677ac5bc9207',
|
|
35
|
-
|
|
35
|
+
stream_name: 'Sales',
|
|
36
36
|
value: 15.30,
|
|
37
37
|
});
|
|
38
38
|
```
|
|
@@ -40,7 +40,7 @@ monitor({
|
|
|
40
40
|
#### In a Server (e.g., Express.js)
|
|
41
41
|
```
|
|
42
42
|
const express = require('express');
|
|
43
|
-
const { initialize, monitor } = require('@chirpier/
|
|
43
|
+
const { initialize, monitor } = require('@chirpier/chirpier-js');
|
|
44
44
|
|
|
45
45
|
const app = express();
|
|
46
46
|
const port = 3000;
|
|
@@ -76,7 +76,7 @@ initialize({ key: 'your-api-key' });
|
|
|
76
76
|
// Monitor an event
|
|
77
77
|
monitor({
|
|
78
78
|
group_id: 'group UUID',
|
|
79
|
-
|
|
79
|
+
stream_name: 'Sales',
|
|
80
80
|
value: 15.3,
|
|
81
81
|
});
|
|
82
82
|
```
|
|
@@ -85,7 +85,3 @@ monitor({
|
|
|
85
85
|
Handling Offline Scenarios
|
|
86
86
|
|
|
87
87
|
The SDK automatically queues events when the network is unavailable and sends them when the connection is restored.
|
|
88
|
-
|
|
89
|
-
## Custom Storage Mechanisms
|
|
90
|
-
|
|
91
|
-
The SDK uses localStorage for browser environments and in memory storage for Node.js. If you need a custom storage mechanism, you can extend the SDK by implementing the Storage interface.
|
|
@@ -43,216 +43,153 @@ var index_1 = require("../index");
|
|
|
43
43
|
var constants_1 = require("../constants");
|
|
44
44
|
var axios_mock_adapter_1 = __importDefault(require("axios-mock-adapter"));
|
|
45
45
|
var axios_1 = __importDefault(require("axios"));
|
|
46
|
-
var server_1 = require("./mocks/server");
|
|
47
|
-
var uuid_1 = require("@lukeed/uuid");
|
|
48
|
-
jest.mock("@lukeed/uuid");
|
|
49
46
|
describe("Chirpier SDK", function () {
|
|
50
|
-
var chirpier;
|
|
51
|
-
afterEach(function () {
|
|
52
|
-
// Clean up mock server
|
|
53
|
-
(0, server_1.cleanupMockServer)();
|
|
54
|
-
});
|
|
55
47
|
describe("Initialization", function () {
|
|
56
|
-
test("should
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
expect(
|
|
64
|
-
expect(chirpier["retries"]).toBe(constants_1.DEFAULT_RETRIES);
|
|
48
|
+
test("should throw error if monitor is called before initialize", function () {
|
|
49
|
+
var event = {
|
|
50
|
+
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
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.");
|
|
65
56
|
});
|
|
66
|
-
test("should initialize with
|
|
67
|
-
|
|
68
|
-
key: "
|
|
57
|
+
test("should initialize with default values", function () {
|
|
58
|
+
(0, index_1.initialize)({
|
|
59
|
+
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
69
60
|
});
|
|
61
|
+
var chirpier = index_1.Chirpier.getInstance({});
|
|
70
62
|
// Setup mock server
|
|
71
63
|
var mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
72
64
|
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
expect(customChirpier["retries"]).toBe(5);
|
|
65
|
+
expect(chirpier === null || chirpier === void 0 ? void 0 : chirpier["apiEndpoint"]).toBe(constants_1.DEFAULT_API_ENDPOINT);
|
|
66
|
+
expect(chirpier === null || chirpier === void 0 ? void 0 : chirpier["retries"]).toBe(constants_1.DEFAULT_RETRIES);
|
|
67
|
+
expect(chirpier === null || chirpier === void 0 ? void 0 : chirpier["timeout"]).toBe(constants_1.DEFAULT_TIMEOUT);
|
|
68
|
+
expect(chirpier === null || chirpier === void 0 ? void 0 : chirpier["batchSize"]).toBe(constants_1.DEFAULT_BATCH_SIZE);
|
|
69
|
+
expect(chirpier === null || chirpier === void 0 ? void 0 : chirpier["flushTimeout"]).toBe(constants_1.DEFAULT_FLUSH_DELAY);
|
|
70
|
+
index_1.Chirpier.stop();
|
|
80
71
|
});
|
|
81
72
|
test("should throw error if key is not provided", function () {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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();
|
|
73
|
+
expect(function () {
|
|
74
|
+
(0, index_1.initialize)({
|
|
75
|
+
key: "api_key",
|
|
76
|
+
});
|
|
77
|
+
}).toThrow(index_1.ChirpierError);
|
|
78
|
+
expect(function () {
|
|
79
|
+
(0, index_1.initialize)({
|
|
80
|
+
key: "api_key",
|
|
81
|
+
});
|
|
82
|
+
}).toThrow("Invalid API key: Not a valid JWT");
|
|
96
83
|
});
|
|
97
84
|
});
|
|
98
85
|
describe("monitor", function () {
|
|
99
86
|
test("event should be sent", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
100
|
-
var mock,
|
|
87
|
+
var mock, event;
|
|
101
88
|
return __generator(this, function (_a) {
|
|
102
89
|
switch (_a.label) {
|
|
103
90
|
case 0:
|
|
104
|
-
chirpier = new index_1.Chirpier({
|
|
105
|
-
key: "api_key",
|
|
106
|
-
});
|
|
107
91
|
mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
108
92
|
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
109
|
-
|
|
110
|
-
|
|
93
|
+
(0, index_1.initialize)({
|
|
94
|
+
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
95
|
+
});
|
|
111
96
|
event = {
|
|
112
97
|
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
113
|
-
|
|
98
|
+
stream_name: "test-stream",
|
|
114
99
|
value: 1,
|
|
115
100
|
};
|
|
116
|
-
|
|
101
|
+
(0, index_1.monitor)(event);
|
|
102
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })];
|
|
117
103
|
case 1:
|
|
118
|
-
_a.sent();
|
|
119
|
-
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 1000); })];
|
|
120
|
-
case 2:
|
|
121
104
|
_a.sent(); // Wait for flush
|
|
122
105
|
expect(mock.history.post.length).toBe(1);
|
|
123
106
|
expect(mock.history.post[0].url).toBe(constants_1.DEFAULT_API_ENDPOINT);
|
|
124
107
|
expect(JSON.parse(mock.history.post[0].data)).toEqual([
|
|
125
108
|
{
|
|
126
109
|
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
127
|
-
|
|
110
|
+
stream_name: "test-stream",
|
|
128
111
|
value: 1,
|
|
129
112
|
},
|
|
130
113
|
]);
|
|
131
114
|
// Clean up the mock
|
|
132
115
|
mock.reset();
|
|
116
|
+
index_1.Chirpier.stop();
|
|
133
117
|
return [2 /*return*/];
|
|
134
118
|
}
|
|
135
119
|
});
|
|
136
120
|
}); });
|
|
137
121
|
test("should throw error for invalid event", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
138
|
-
var
|
|
122
|
+
var chirpier, invalidEvent;
|
|
139
123
|
return __generator(this, function (_a) {
|
|
140
124
|
switch (_a.label) {
|
|
141
125
|
case 0:
|
|
142
|
-
|
|
143
|
-
key: "
|
|
126
|
+
(0, index_1.initialize)({
|
|
127
|
+
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
144
128
|
});
|
|
129
|
+
chirpier = index_1.Chirpier.getInstance({});
|
|
145
130
|
invalidEvent = {
|
|
146
131
|
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
147
132
|
};
|
|
148
|
-
return [4 /*yield*/, expect(chirpier.monitor(invalidEvent)).rejects.toThrow(index_1.ChirpierError)];
|
|
133
|
+
return [4 /*yield*/, expect(chirpier === null || chirpier === void 0 ? void 0 : chirpier.monitor(invalidEvent)).rejects.toThrow(index_1.ChirpierError)];
|
|
149
134
|
case 1:
|
|
150
135
|
_a.sent();
|
|
151
|
-
|
|
152
|
-
|
|
136
|
+
// Clean up the mock
|
|
137
|
+
index_1.Chirpier.stop();
|
|
153
138
|
return [2 /*return*/];
|
|
154
139
|
}
|
|
155
140
|
});
|
|
156
141
|
}); });
|
|
157
142
|
test("should batch events and flush when batch size is reached", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
158
|
-
var mock,
|
|
143
|
+
var mock, event;
|
|
159
144
|
return __generator(this, function (_a) {
|
|
160
145
|
switch (_a.label) {
|
|
161
146
|
case 0:
|
|
162
147
|
mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
163
148
|
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
164
|
-
|
|
165
|
-
|
|
149
|
+
(0, index_1.initialize)({
|
|
150
|
+
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
151
|
+
});
|
|
166
152
|
event = {
|
|
167
153
|
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
168
|
-
|
|
154
|
+
stream_name: "test-stream",
|
|
169
155
|
value: 1,
|
|
170
156
|
};
|
|
171
157
|
(0, index_1.monitor)(event);
|
|
172
158
|
(0, index_1.monitor)(event);
|
|
173
|
-
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve,
|
|
159
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })];
|
|
174
160
|
case 1:
|
|
175
161
|
_a.sent(); // Wait for flush
|
|
176
162
|
expect(mock.history.post.length).toBe(1);
|
|
177
163
|
expect(JSON.parse(mock.history.post[0].data).length).toBe(2);
|
|
178
164
|
mock.reset();
|
|
165
|
+
index_1.Chirpier.stop();
|
|
179
166
|
return [2 /*return*/];
|
|
180
167
|
}
|
|
181
168
|
});
|
|
182
169
|
}); });
|
|
183
170
|
test("should flush events after interval", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
184
|
-
var mock,
|
|
171
|
+
var mock, event;
|
|
185
172
|
return __generator(this, function (_a) {
|
|
186
173
|
switch (_a.label) {
|
|
187
174
|
case 0:
|
|
188
175
|
mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
189
176
|
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
190
|
-
|
|
191
|
-
|
|
177
|
+
(0, index_1.initialize)({
|
|
178
|
+
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
179
|
+
});
|
|
192
180
|
event = {
|
|
193
181
|
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
194
|
-
|
|
182
|
+
stream_name: "test-stream",
|
|
195
183
|
value: 1,
|
|
196
184
|
};
|
|
197
185
|
(0, index_1.monitor)(event);
|
|
198
|
-
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve,
|
|
186
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })];
|
|
199
187
|
case 1:
|
|
200
188
|
_a.sent(); // Wait for flush
|
|
201
189
|
expect(mock.history.post.length).toBe(1);
|
|
202
190
|
expect(JSON.parse(mock.history.post[0].data).length).toBe(1);
|
|
203
191
|
mock.reset();
|
|
204
|
-
|
|
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");
|
|
255
|
-
mock.reset();
|
|
192
|
+
index_1.Chirpier.stop();
|
|
256
193
|
return [2 /*return*/];
|
|
257
194
|
}
|
|
258
195
|
});
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export declare const DEFAULT_API_ENDPOINT = "https://events.chirpier.co/
|
|
1
|
+
export declare const DEFAULT_API_ENDPOINT = "https://events.chirpier.co/v1.0/events";
|
|
2
2
|
export declare const DEFAULT_RETRIES = 30;
|
|
3
3
|
export declare const DEFAULT_TIMEOUT = 5000;
|
|
4
|
+
export declare const DEFAULT_BATCH_SIZE = 50;
|
|
5
|
+
export declare const DEFAULT_FLUSH_DELAY = 500;
|
|
4
6
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,oBAAoB,
|
|
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"}
|
package/dist/constants.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// constants.ts
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.DEFAULT_TIMEOUT = exports.DEFAULT_RETRIES = exports.DEFAULT_API_ENDPOINT = void 0;
|
|
5
|
-
exports.DEFAULT_API_ENDPOINT = 'https://events.chirpier.co/
|
|
4
|
+
exports.DEFAULT_FLUSH_DELAY = exports.DEFAULT_BATCH_SIZE = exports.DEFAULT_TIMEOUT = exports.DEFAULT_RETRIES = exports.DEFAULT_API_ENDPOINT = void 0;
|
|
5
|
+
exports.DEFAULT_API_ENDPOINT = 'https://events.chirpier.co/v1.0/events';
|
|
6
6
|
exports.DEFAULT_RETRIES = 30;
|
|
7
7
|
exports.DEFAULT_TIMEOUT = 5000;
|
|
8
|
+
exports.DEFAULT_BATCH_SIZE = 50;
|
|
9
|
+
exports.DEFAULT_FLUSH_DELAY = 500;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
interface Options {
|
|
2
2
|
key: string;
|
|
3
|
-
apiEndpoint?: string;
|
|
4
|
-
retries?: number;
|
|
5
|
-
timeout?: number;
|
|
6
|
-
batchSize?: number;
|
|
7
|
-
flushInterval?: number;
|
|
8
3
|
}
|
|
9
4
|
export interface Event {
|
|
10
5
|
group_id: string;
|
|
11
|
-
|
|
6
|
+
stream_name: string;
|
|
12
7
|
value: number;
|
|
13
|
-
event_id?: string;
|
|
14
8
|
}
|
|
15
9
|
export declare class ChirpierError extends Error {
|
|
16
10
|
constructor(message: string);
|
|
@@ -19,20 +13,27 @@ export declare class ChirpierError extends Error {
|
|
|
19
13
|
* Main Chirpier class for monitoring events.
|
|
20
14
|
*/
|
|
21
15
|
export declare class Chirpier {
|
|
16
|
+
private static instance;
|
|
22
17
|
private readonly apiKey;
|
|
23
18
|
private readonly apiEndpoint;
|
|
24
19
|
private readonly retries;
|
|
25
20
|
private readonly timeout;
|
|
26
21
|
private readonly axiosInstance;
|
|
27
22
|
private eventQueue;
|
|
28
|
-
private flushTimeout;
|
|
29
23
|
private readonly batchSize;
|
|
30
|
-
private readonly
|
|
24
|
+
private readonly flushTimeout;
|
|
25
|
+
private flushTimeoutId;
|
|
31
26
|
/**
|
|
32
27
|
* Initializes a new instance of the Chirpier class.
|
|
33
28
|
* @param options - Configuration options for the SDK.
|
|
34
29
|
*/
|
|
35
|
-
constructor(
|
|
30
|
+
private constructor();
|
|
31
|
+
/**
|
|
32
|
+
* Gets the singleton instance of Chirpier, creating it if it doesn't exist.
|
|
33
|
+
* @param options - Configuration options for the SDK.
|
|
34
|
+
* @returns The Chirpier instance.
|
|
35
|
+
*/
|
|
36
|
+
static getInstance(options: Options): Chirpier | null;
|
|
36
37
|
/**
|
|
37
38
|
* Validates the event structure.
|
|
38
39
|
* @param event - The event to validate.
|
|
@@ -53,6 +54,7 @@ export declare class Chirpier {
|
|
|
53
54
|
* @param events - The array of events to send.
|
|
54
55
|
*/
|
|
55
56
|
private sendEvents;
|
|
57
|
+
static stop(): void;
|
|
56
58
|
}
|
|
57
59
|
/**
|
|
58
60
|
* 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":"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"}
|
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) {
|
|
@@ -78,7 +67,6 @@ exports.monitor = exports.initialize = exports.Chirpier = exports.ChirpierError
|
|
|
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");
|
|
84
72
|
// Custom error class for Chirpier-specific errors
|
|
@@ -101,19 +89,19 @@ var Chirpier = /** @class */ (function () {
|
|
|
101
89
|
* Initializes a new instance of the Chirpier class.
|
|
102
90
|
* @param options - Configuration options for the SDK.
|
|
103
91
|
*/
|
|
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;
|
|
92
|
+
function Chirpier(options) {
|
|
106
93
|
this.eventQueue = [];
|
|
107
|
-
this.
|
|
94
|
+
this.flushTimeoutId = null;
|
|
95
|
+
var key = options.key;
|
|
108
96
|
if (!key || typeof key !== "string") {
|
|
109
97
|
throw new ChirpierError("API key is required and must be a string");
|
|
110
98
|
}
|
|
111
99
|
this.apiKey = key;
|
|
112
|
-
this.apiEndpoint =
|
|
113
|
-
this.retries =
|
|
114
|
-
this.timeout =
|
|
115
|
-
this.batchSize =
|
|
116
|
-
this.
|
|
100
|
+
this.apiEndpoint = constants_1.DEFAULT_API_ENDPOINT;
|
|
101
|
+
this.retries = constants_1.DEFAULT_RETRIES;
|
|
102
|
+
this.timeout = constants_1.DEFAULT_TIMEOUT;
|
|
103
|
+
this.batchSize = constants_1.DEFAULT_BATCH_SIZE;
|
|
104
|
+
this.flushTimeout = constants_1.DEFAULT_FLUSH_DELAY;
|
|
117
105
|
// Create axios instance with authorization header
|
|
118
106
|
this.axiosInstance = axios_1.default.create({
|
|
119
107
|
headers: { Authorization: "Bearer ".concat(this.apiKey) },
|
|
@@ -136,6 +124,17 @@ var Chirpier = /** @class */ (function () {
|
|
|
136
124
|
shouldResetTimeout: true,
|
|
137
125
|
});
|
|
138
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Gets the singleton instance of Chirpier, creating it if it doesn't exist.
|
|
129
|
+
* @param options - Configuration options for the SDK.
|
|
130
|
+
* @returns The Chirpier instance.
|
|
131
|
+
*/
|
|
132
|
+
Chirpier.getInstance = function (options) {
|
|
133
|
+
if (!Chirpier.instance && options.key) {
|
|
134
|
+
Chirpier.instance = new Chirpier(options);
|
|
135
|
+
}
|
|
136
|
+
return Chirpier.instance;
|
|
137
|
+
};
|
|
139
138
|
/**
|
|
140
139
|
* Validates the event structure.
|
|
141
140
|
* @param event - The event to validate.
|
|
@@ -145,8 +144,8 @@ var Chirpier = /** @class */ (function () {
|
|
|
145
144
|
return (typeof event.group_id === "string" &&
|
|
146
145
|
/^[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
146
|
event.group_id.trim().length > 0 &&
|
|
148
|
-
typeof event.
|
|
149
|
-
event.
|
|
147
|
+
typeof event.stream_name === "string" &&
|
|
148
|
+
event.stream_name.trim().length > 0 &&
|
|
150
149
|
typeof event.value === "number");
|
|
151
150
|
};
|
|
152
151
|
/**
|
|
@@ -155,24 +154,27 @@ var Chirpier = /** @class */ (function () {
|
|
|
155
154
|
*/
|
|
156
155
|
Chirpier.prototype.monitor = function (event) {
|
|
157
156
|
return __awaiter(this, void 0, void 0, function () {
|
|
158
|
-
var eventWithID;
|
|
159
157
|
var _this = this;
|
|
160
158
|
return __generator(this, function (_a) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
159
|
+
switch (_a.label) {
|
|
160
|
+
case 0:
|
|
161
|
+
if (!this.isValidEvent(event)) {
|
|
162
|
+
throw new ChirpierError("Invalid event format. Must include group_id, stream_name, and numeric value.");
|
|
163
|
+
}
|
|
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()];
|
|
168
|
+
case 1:
|
|
169
|
+
_a.sent();
|
|
170
|
+
return [3 /*break*/, 3];
|
|
171
|
+
case 2:
|
|
172
|
+
if (!this.flushTimeoutId) {
|
|
173
|
+
this.flushTimeoutId = setTimeout(function () { return _this.flushQueue(); }, this.flushTimeout);
|
|
174
|
+
}
|
|
175
|
+
_a.label = 3;
|
|
176
|
+
case 3: return [2 /*return*/];
|
|
174
177
|
}
|
|
175
|
-
return [2 /*return*/];
|
|
176
178
|
});
|
|
177
179
|
});
|
|
178
180
|
};
|
|
@@ -182,17 +184,16 @@ var Chirpier = /** @class */ (function () {
|
|
|
182
184
|
Chirpier.prototype.flushQueue = function () {
|
|
183
185
|
return __awaiter(this, void 0, void 0, function () {
|
|
184
186
|
var eventsToSend, error_1;
|
|
185
|
-
var _this = this;
|
|
186
187
|
return __generator(this, function (_a) {
|
|
187
188
|
switch (_a.label) {
|
|
188
189
|
case 0:
|
|
190
|
+
if (this.flushTimeoutId) {
|
|
191
|
+
clearTimeout(this.flushTimeoutId);
|
|
192
|
+
this.flushTimeoutId = null;
|
|
193
|
+
}
|
|
189
194
|
if (this.eventQueue.length === 0) {
|
|
190
195
|
return [2 /*return*/];
|
|
191
196
|
}
|
|
192
|
-
if (this.flushTimeout) {
|
|
193
|
-
clearTimeout(this.flushTimeout);
|
|
194
|
-
this.flushTimeout = null;
|
|
195
|
-
}
|
|
196
197
|
eventsToSend = __spreadArray([], this.eventQueue, true);
|
|
197
198
|
this.eventQueue = [];
|
|
198
199
|
_a.label = 1;
|
|
@@ -207,12 +208,7 @@ var Chirpier = /** @class */ (function () {
|
|
|
207
208
|
error_1 = _a.sent();
|
|
208
209
|
console.error("Failed to send events:", error_1);
|
|
209
210
|
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
|
-
return [2 /*return*/];
|
|
211
|
+
case 4: return [2 /*return*/];
|
|
216
212
|
}
|
|
217
213
|
});
|
|
218
214
|
});
|
|
@@ -233,6 +229,21 @@ var Chirpier = /** @class */ (function () {
|
|
|
233
229
|
});
|
|
234
230
|
});
|
|
235
231
|
};
|
|
232
|
+
// Stop the timeout and uninitialize the Chirpier instance
|
|
233
|
+
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;
|
|
245
|
+
};
|
|
246
|
+
Chirpier.instance = null;
|
|
236
247
|
return Chirpier;
|
|
237
248
|
}());
|
|
238
249
|
exports.Chirpier = Chirpier;
|
|
@@ -270,8 +281,6 @@ function isValidJWT(token) {
|
|
|
270
281
|
return false;
|
|
271
282
|
}
|
|
272
283
|
}
|
|
273
|
-
// Singleton instance of Chirpier
|
|
274
|
-
var chirpierInstance = null;
|
|
275
284
|
/**
|
|
276
285
|
* Initializes the Chirpier SDK.
|
|
277
286
|
* @param options - Configuration options for the SDK.
|
|
@@ -281,7 +290,7 @@ function initialize(options) {
|
|
|
281
290
|
throw new ChirpierError("Invalid API key: Not a valid JWT");
|
|
282
291
|
}
|
|
283
292
|
try {
|
|
284
|
-
|
|
293
|
+
Chirpier.getInstance(options);
|
|
285
294
|
}
|
|
286
295
|
catch (error) {
|
|
287
296
|
if (error instanceof ChirpierError) {
|
|
@@ -299,10 +308,11 @@ exports.initialize = initialize;
|
|
|
299
308
|
* @param event - The event to monitor.
|
|
300
309
|
*/
|
|
301
310
|
function monitor(event) {
|
|
302
|
-
|
|
311
|
+
var instance = Chirpier.getInstance({});
|
|
312
|
+
if (!instance) {
|
|
303
313
|
throw new ChirpierError("Chirpier SDK is not initialized. Please call initialize() first.");
|
|
304
314
|
}
|
|
305
|
-
|
|
315
|
+
instance.monitor(event).catch(function (error) {
|
|
306
316
|
console.error("Error in monitor function:", error);
|
|
307
317
|
});
|
|
308
318
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chirpier/chirpier-js",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Chirpier SDK for JavaScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chirpier",
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
"test": "jest --coverage"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@lukeed/uuid": "^2.0.1",
|
|
31
30
|
"axios": "^0.24.0",
|
|
32
31
|
"axios-retry": "^4.5.0",
|
|
33
32
|
"js-base64": "^3.7.7",
|
|
@@ -35,15 +34,19 @@
|
|
|
35
34
|
"tslib": "^2.3.0"
|
|
36
35
|
},
|
|
37
36
|
"devDependencies": {
|
|
37
|
+
"@eslint/js": "^9.15.0",
|
|
38
38
|
"@jest/globals": "^29.7.0",
|
|
39
39
|
"@types/jest": "^29.5.13",
|
|
40
40
|
"@types/mocha": "^10.0.8",
|
|
41
41
|
"@types/node": "^22.7.5",
|
|
42
42
|
"@types/uuid": "^10.0.0",
|
|
43
43
|
"axios-mock-adapter": "^2.0.0",
|
|
44
|
+
"eslint": "^9.15.0",
|
|
45
|
+
"globals": "^15.12.0",
|
|
44
46
|
"jest": "^29.7.0",
|
|
45
47
|
"ts-jest": "^29.2.4",
|
|
46
48
|
"typescript": "^4.4.3",
|
|
49
|
+
"typescript-eslint": "^8.15.0",
|
|
47
50
|
"webpack": "^5.52.0",
|
|
48
51
|
"webpack-cli": "^4.8.0"
|
|
49
52
|
}
|
|
@@ -1,222 +1,163 @@
|
|
|
1
1
|
import { Chirpier, ChirpierError, Event, initialize, monitor } from "../index";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_API_ENDPOINT,
|
|
4
|
+
DEFAULT_RETRIES,
|
|
5
|
+
DEFAULT_TIMEOUT,
|
|
6
|
+
DEFAULT_BATCH_SIZE,
|
|
7
|
+
DEFAULT_FLUSH_DELAY,
|
|
8
|
+
} from "../constants";
|
|
3
9
|
import MockAdapter from "axios-mock-adapter";
|
|
4
10
|
import axios from "axios";
|
|
5
|
-
import { cleanupMockServer } from "./mocks/server";
|
|
6
|
-
import { v4 as uuidv4 } from "@lukeed/uuid";
|
|
7
|
-
|
|
8
|
-
jest.mock("@lukeed/uuid");
|
|
9
11
|
|
|
10
12
|
describe("Chirpier SDK", () => {
|
|
11
|
-
let chirpier: Chirpier;
|
|
12
|
-
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
// Clean up mock server
|
|
15
|
-
cleanupMockServer();
|
|
16
|
-
});
|
|
17
|
-
|
|
18
13
|
describe("Initialization", () => {
|
|
19
|
-
test("should
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const mock = new MockAdapter(axios);
|
|
26
|
-
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
14
|
+
test("should throw error if monitor is called before initialize", () => {
|
|
15
|
+
const event: Event = {
|
|
16
|
+
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
17
|
+
stream_name: "test-stream",
|
|
18
|
+
value: 1,
|
|
19
|
+
};
|
|
27
20
|
|
|
28
|
-
expect(
|
|
29
|
-
expect(
|
|
21
|
+
expect(() => monitor(event)).toThrow(ChirpierError);
|
|
22
|
+
expect(() => monitor(event)).toThrow(
|
|
23
|
+
"Chirpier SDK is not initialized. Please call initialize() first."
|
|
24
|
+
);
|
|
30
25
|
});
|
|
31
26
|
|
|
32
|
-
test("should initialize with
|
|
33
|
-
|
|
34
|
-
key: "
|
|
27
|
+
test("should initialize with default values", () => {
|
|
28
|
+
initialize({
|
|
29
|
+
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
35
30
|
});
|
|
31
|
+
const chirpier = Chirpier.getInstance({} as any);
|
|
36
32
|
|
|
37
33
|
// Setup mock server
|
|
38
34
|
const mock = new MockAdapter(axios);
|
|
39
35
|
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
40
36
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
expect(chirpier?.["apiEndpoint"]).toBe(DEFAULT_API_ENDPOINT);
|
|
38
|
+
expect(chirpier?.["retries"]).toBe(DEFAULT_RETRIES);
|
|
39
|
+
expect(chirpier?.["timeout"]).toBe(DEFAULT_TIMEOUT);
|
|
40
|
+
expect(chirpier?.["batchSize"]).toBe(DEFAULT_BATCH_SIZE);
|
|
41
|
+
expect(chirpier?.["flushTimeout"]).toBe(DEFAULT_FLUSH_DELAY);
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
expect(customChirpier["retries"]).toBe(5);
|
|
43
|
+
Chirpier.stop();
|
|
49
44
|
});
|
|
50
45
|
|
|
51
46
|
test("should throw error if key is not provided", () => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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();
|
|
47
|
+
expect(() => {
|
|
48
|
+
initialize({
|
|
49
|
+
key: "api_key",
|
|
50
|
+
});
|
|
51
|
+
}).toThrow(ChirpierError);
|
|
52
|
+
expect(() => {
|
|
53
|
+
initialize({
|
|
54
|
+
key: "api_key",
|
|
55
|
+
});
|
|
56
|
+
}).toThrow("Invalid API key: Not a valid JWT");
|
|
70
57
|
});
|
|
71
58
|
});
|
|
72
59
|
|
|
73
60
|
describe("monitor", () => {
|
|
74
61
|
test("event should be sent", async () => {
|
|
75
|
-
chirpier = new Chirpier({
|
|
76
|
-
key: "api_key",
|
|
77
|
-
});
|
|
78
|
-
|
|
79
62
|
// Setup mock server
|
|
80
63
|
const mock = new MockAdapter(axios);
|
|
81
64
|
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
82
65
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
66
|
+
initialize({
|
|
67
|
+
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
68
|
+
});
|
|
69
|
+
|
|
86
70
|
const event: Event = {
|
|
87
71
|
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
88
|
-
|
|
72
|
+
stream_name: "test-stream",
|
|
89
73
|
value: 1,
|
|
90
74
|
};
|
|
91
75
|
|
|
92
|
-
|
|
76
|
+
monitor(event);
|
|
93
77
|
|
|
94
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
78
|
+
await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for flush
|
|
95
79
|
|
|
96
80
|
expect(mock.history.post.length).toBe(1);
|
|
97
81
|
expect(mock.history.post[0].url).toBe(DEFAULT_API_ENDPOINT);
|
|
98
82
|
expect(JSON.parse(mock.history.post[0].data)).toEqual([
|
|
99
83
|
{
|
|
100
84
|
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
101
|
-
|
|
85
|
+
stream_name: "test-stream",
|
|
102
86
|
value: 1,
|
|
103
87
|
},
|
|
104
88
|
]);
|
|
105
89
|
|
|
106
90
|
// Clean up the mock
|
|
107
91
|
mock.reset();
|
|
92
|
+
Chirpier.stop();
|
|
108
93
|
});
|
|
109
94
|
|
|
110
95
|
test("should throw error for invalid event", async () => {
|
|
111
|
-
|
|
112
|
-
key: "
|
|
96
|
+
initialize({
|
|
97
|
+
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
113
98
|
});
|
|
99
|
+
const chirpier = Chirpier.getInstance({} as any);
|
|
100
|
+
|
|
114
101
|
const invalidEvent = {
|
|
115
102
|
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
116
103
|
} as any;
|
|
117
|
-
await expect(chirpier
|
|
104
|
+
await expect(chirpier?.monitor(invalidEvent)).rejects.toThrow(
|
|
118
105
|
ChirpierError
|
|
119
106
|
);
|
|
120
107
|
|
|
121
108
|
// Clean up the mock
|
|
122
|
-
|
|
123
|
-
mock.reset();
|
|
109
|
+
Chirpier.stop();
|
|
124
110
|
});
|
|
125
111
|
|
|
126
112
|
test("should batch events and flush when batch size is reached", async () => {
|
|
127
113
|
const mock = new MockAdapter(axios);
|
|
128
114
|
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
129
115
|
|
|
130
|
-
|
|
131
|
-
|
|
116
|
+
initialize({
|
|
117
|
+
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
118
|
+
});
|
|
132
119
|
|
|
133
120
|
const event: Event = {
|
|
134
121
|
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
135
|
-
|
|
122
|
+
stream_name: "test-stream",
|
|
136
123
|
value: 1,
|
|
137
124
|
};
|
|
138
125
|
|
|
139
126
|
monitor(event);
|
|
140
127
|
monitor(event);
|
|
141
128
|
|
|
142
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
129
|
+
await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for flush
|
|
143
130
|
|
|
144
131
|
expect(mock.history.post.length).toBe(1);
|
|
145
132
|
expect(JSON.parse(mock.history.post[0].data).length).toBe(2);
|
|
146
133
|
|
|
147
134
|
mock.reset();
|
|
135
|
+
Chirpier.stop();
|
|
148
136
|
});
|
|
149
137
|
|
|
150
138
|
test("should flush events after interval", async () => {
|
|
151
139
|
const mock = new MockAdapter(axios);
|
|
152
140
|
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
153
141
|
|
|
154
|
-
|
|
155
|
-
|
|
142
|
+
initialize({
|
|
143
|
+
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
144
|
+
});
|
|
156
145
|
|
|
157
146
|
const event: Event = {
|
|
158
147
|
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
159
|
-
|
|
148
|
+
stream_name: "test-stream",
|
|
160
149
|
value: 1,
|
|
161
150
|
};
|
|
162
151
|
|
|
163
152
|
monitor(event);
|
|
164
153
|
|
|
165
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
154
|
+
await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for flush
|
|
166
155
|
|
|
167
156
|
expect(mock.history.post.length).toBe(1);
|
|
168
157
|
expect(JSON.parse(mock.history.post[0].data).length).toBe(1);
|
|
169
158
|
|
|
170
159
|
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
|
-
|
|
219
|
-
mock.reset();
|
|
160
|
+
Chirpier.stop();
|
|
220
161
|
});
|
|
221
162
|
});
|
|
222
|
-
});
|
|
163
|
+
});
|
package/src/constants.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// constants.ts
|
|
2
2
|
|
|
3
|
-
export const DEFAULT_API_ENDPOINT = 'https://events.chirpier.co/
|
|
3
|
+
export const DEFAULT_API_ENDPOINT = 'https://events.chirpier.co/v1.0/events';
|
|
4
4
|
export const DEFAULT_RETRIES = 30;
|
|
5
|
-
export const DEFAULT_TIMEOUT = 5000
|
|
5
|
+
export const DEFAULT_TIMEOUT = 5000
|
|
6
|
+
export const DEFAULT_BATCH_SIZE = 50;
|
|
7
|
+
export const DEFAULT_FLUSH_DELAY = 500;
|
package/src/index.ts
CHANGED
|
@@ -1,30 +1,25 @@
|
|
|
1
1
|
// Import necessary dependencies
|
|
2
2
|
import axios, { AxiosInstance } from "axios";
|
|
3
3
|
import axiosRetry from "axios-retry";
|
|
4
|
-
import { v4 as uuidv4 } from "@lukeed/uuid";
|
|
5
4
|
import { Base64 } from "js-base64";
|
|
6
5
|
import {
|
|
7
6
|
DEFAULT_API_ENDPOINT,
|
|
8
7
|
DEFAULT_RETRIES,
|
|
9
8
|
DEFAULT_TIMEOUT,
|
|
9
|
+
DEFAULT_BATCH_SIZE,
|
|
10
|
+
DEFAULT_FLUSH_DELAY,
|
|
10
11
|
} from "./constants";
|
|
11
12
|
|
|
12
13
|
// Define the options interface for Chirpier initialization
|
|
13
14
|
interface Options {
|
|
14
15
|
key: string;
|
|
15
|
-
apiEndpoint?: string;
|
|
16
|
-
retries?: number;
|
|
17
|
-
timeout?: number;
|
|
18
|
-
batchSize?: number;
|
|
19
|
-
flushInterval?: number;
|
|
20
16
|
}
|
|
21
17
|
|
|
22
18
|
// Define the Event interface for monitoring
|
|
23
19
|
export interface Event {
|
|
24
20
|
group_id: string;
|
|
25
|
-
|
|
21
|
+
stream_name: string;
|
|
26
22
|
value: number;
|
|
27
|
-
event_id?: string;
|
|
28
23
|
}
|
|
29
24
|
|
|
30
25
|
// Custom error class for Chirpier-specific errors
|
|
@@ -40,37 +35,36 @@ export class ChirpierError extends Error {
|
|
|
40
35
|
* Main Chirpier class for monitoring events.
|
|
41
36
|
*/
|
|
42
37
|
export class Chirpier {
|
|
38
|
+
private static instance: Chirpier | null = null;
|
|
43
39
|
private readonly apiKey: string;
|
|
44
40
|
private readonly apiEndpoint: string;
|
|
45
41
|
private readonly retries: number;
|
|
46
42
|
private readonly timeout: number;
|
|
47
43
|
private readonly axiosInstance: AxiosInstance;
|
|
48
44
|
private eventQueue: Event[] = [];
|
|
49
|
-
private flushTimeout: NodeJS.Timeout | null = null;
|
|
50
45
|
private readonly batchSize: number;
|
|
51
|
-
private readonly
|
|
46
|
+
private readonly flushTimeout: number;
|
|
47
|
+
private flushTimeoutId: NodeJS.Timeout | null = null;
|
|
52
48
|
|
|
53
49
|
/**
|
|
54
50
|
* Initializes a new instance of the Chirpier class.
|
|
55
51
|
* @param options - Configuration options for the SDK.
|
|
56
52
|
*/
|
|
57
|
-
constructor({
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
batchSize = 100,
|
|
63
|
-
flushInterval = 500,
|
|
64
|
-
}: Options) {
|
|
53
|
+
private constructor(options: Options) {
|
|
54
|
+
const {
|
|
55
|
+
key,
|
|
56
|
+
} = options;
|
|
57
|
+
|
|
65
58
|
if (!key || typeof key !== "string") {
|
|
66
59
|
throw new ChirpierError("API key is required and must be a string");
|
|
67
60
|
}
|
|
61
|
+
|
|
68
62
|
this.apiKey = key;
|
|
69
|
-
this.apiEndpoint =
|
|
70
|
-
this.retries =
|
|
71
|
-
this.timeout =
|
|
72
|
-
this.batchSize =
|
|
73
|
-
this.
|
|
63
|
+
this.apiEndpoint = DEFAULT_API_ENDPOINT;
|
|
64
|
+
this.retries = DEFAULT_RETRIES;
|
|
65
|
+
this.timeout = DEFAULT_TIMEOUT;
|
|
66
|
+
this.batchSize = DEFAULT_BATCH_SIZE;
|
|
67
|
+
this.flushTimeout = DEFAULT_FLUSH_DELAY;
|
|
74
68
|
|
|
75
69
|
// Create axios instance with authorization header
|
|
76
70
|
this.axiosInstance = axios.create({
|
|
@@ -102,6 +96,18 @@ export class Chirpier {
|
|
|
102
96
|
});
|
|
103
97
|
}
|
|
104
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Gets the singleton instance of Chirpier, creating it if it doesn't exist.
|
|
101
|
+
* @param options - Configuration options for the SDK.
|
|
102
|
+
* @returns The Chirpier instance.
|
|
103
|
+
*/
|
|
104
|
+
public static getInstance(options: Options): Chirpier | null {
|
|
105
|
+
if (!Chirpier.instance && options.key) {
|
|
106
|
+
Chirpier.instance = new Chirpier(options);
|
|
107
|
+
}
|
|
108
|
+
return Chirpier.instance;
|
|
109
|
+
}
|
|
110
|
+
|
|
105
111
|
/**
|
|
106
112
|
* Validates the event structure.
|
|
107
113
|
* @param event - The event to validate.
|
|
@@ -114,8 +120,8 @@ export class Chirpier {
|
|
|
114
120
|
event.group_id
|
|
115
121
|
) &&
|
|
116
122
|
event.group_id.trim().length > 0 &&
|
|
117
|
-
typeof event.
|
|
118
|
-
event.
|
|
123
|
+
typeof event.stream_name === "string" &&
|
|
124
|
+
event.stream_name.trim().length > 0 &&
|
|
119
125
|
typeof event.value === "number"
|
|
120
126
|
);
|
|
121
127
|
}
|
|
@@ -125,28 +131,19 @@ export class Chirpier {
|
|
|
125
131
|
* @param event - The event to monitor.
|
|
126
132
|
*/
|
|
127
133
|
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
|
-
|
|
132
134
|
if (!this.isValidEvent(event)) {
|
|
133
135
|
throw new ChirpierError(
|
|
134
|
-
"Invalid event format. Must include group_id,
|
|
136
|
+
"Invalid event format. Must include group_id, stream_name, and numeric value."
|
|
135
137
|
);
|
|
136
138
|
}
|
|
137
139
|
|
|
138
|
-
|
|
139
|
-
const eventWithID = { ...event, event_id: event.event_id || uuidv4() };
|
|
140
|
-
|
|
141
|
-
this.eventQueue.push(eventWithID);
|
|
140
|
+
this.eventQueue.push(event);
|
|
142
141
|
|
|
143
142
|
if (this.eventQueue.length >= this.batchSize) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
this.flushInterval
|
|
149
|
-
);
|
|
143
|
+
console.info(`Batch size reached. Flushing queue.`);
|
|
144
|
+
await this.flushQueue();
|
|
145
|
+
} else if (!this.flushTimeoutId) {
|
|
146
|
+
this.flushTimeoutId = setTimeout(() => this.flushQueue(), this.flushTimeout);
|
|
150
147
|
}
|
|
151
148
|
}
|
|
152
149
|
|
|
@@ -154,14 +151,14 @@ export class Chirpier {
|
|
|
154
151
|
* Flushes the event queue by sending all events to the API.
|
|
155
152
|
*/
|
|
156
153
|
private async flushQueue(): Promise<void> {
|
|
154
|
+
if (this.flushTimeoutId) {
|
|
155
|
+
clearTimeout(this.flushTimeoutId);
|
|
156
|
+
this.flushTimeoutId = null;
|
|
157
|
+
}
|
|
158
|
+
|
|
157
159
|
if (this.eventQueue.length === 0) {
|
|
158
160
|
return;
|
|
159
161
|
}
|
|
160
|
-
|
|
161
|
-
if (this.flushTimeout) {
|
|
162
|
-
clearTimeout(this.flushTimeout);
|
|
163
|
-
this.flushTimeout = null;
|
|
164
|
-
}
|
|
165
162
|
|
|
166
163
|
const eventsToSend = [...this.eventQueue];
|
|
167
164
|
this.eventQueue = [];
|
|
@@ -172,14 +169,6 @@ export class Chirpier {
|
|
|
172
169
|
} catch (error) {
|
|
173
170
|
console.error("Failed to send events:", error);
|
|
174
171
|
}
|
|
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
|
-
}
|
|
183
172
|
}
|
|
184
173
|
|
|
185
174
|
/**
|
|
@@ -189,6 +178,21 @@ export class Chirpier {
|
|
|
189
178
|
private async sendEvents(events: Event[]): Promise<void> {
|
|
190
179
|
await this.axiosInstance.post(this.apiEndpoint, events);
|
|
191
180
|
}
|
|
181
|
+
|
|
182
|
+
// Stop the timeout and uninitialize the Chirpier instance
|
|
183
|
+
public static stop(): void {
|
|
184
|
+
if (!Chirpier.instance) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (Chirpier.instance.flushTimeoutId) {
|
|
188
|
+
clearTimeout(Chirpier.instance.flushTimeoutId);
|
|
189
|
+
Chirpier.instance.flushTimeoutId = null;
|
|
190
|
+
}
|
|
191
|
+
// Flush any remaining events in the queue
|
|
192
|
+
Chirpier.instance.flushQueue();
|
|
193
|
+
// Uninitialize the Chirpier instance
|
|
194
|
+
Chirpier.instance = null;
|
|
195
|
+
}
|
|
192
196
|
}
|
|
193
197
|
|
|
194
198
|
/**
|
|
@@ -226,9 +230,6 @@ function isValidJWT(token: string): boolean {
|
|
|
226
230
|
}
|
|
227
231
|
}
|
|
228
232
|
|
|
229
|
-
// Singleton instance of Chirpier
|
|
230
|
-
let chirpierInstance: Chirpier | null = null;
|
|
231
|
-
|
|
232
233
|
/**
|
|
233
234
|
* Initializes the Chirpier SDK.
|
|
234
235
|
* @param options - Configuration options for the SDK.
|
|
@@ -239,7 +240,7 @@ export function initialize(options: Options): void {
|
|
|
239
240
|
}
|
|
240
241
|
|
|
241
242
|
try {
|
|
242
|
-
|
|
243
|
+
Chirpier.getInstance(options);
|
|
243
244
|
} catch (error) {
|
|
244
245
|
if (error instanceof ChirpierError) {
|
|
245
246
|
console.error("Failed to initialize Chirpier SDK:", error.message);
|
|
@@ -258,12 +259,14 @@ export function initialize(options: Options): void {
|
|
|
258
259
|
* @param event - The event to monitor.
|
|
259
260
|
*/
|
|
260
261
|
export function monitor(event: Event): void {
|
|
261
|
-
|
|
262
|
+
const instance = Chirpier.getInstance({} as Options);
|
|
263
|
+
if (!instance) {
|
|
262
264
|
throw new ChirpierError(
|
|
263
265
|
"Chirpier SDK is not initialized. Please call initialize() first."
|
|
264
266
|
);
|
|
265
267
|
}
|
|
266
|
-
|
|
268
|
+
|
|
269
|
+
instance.monitor(event).catch((error) => {
|
|
267
270
|
console.error("Error in monitor function:", error);
|
|
268
271
|
});
|
|
269
272
|
}
|