@chirpier/chirpier-js 0.1.4 → 0.1.6
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 +83 -14
- package/dist/__tests__/chirpier.test.js +24 -14
- package/dist/constants.d.ts +3 -3
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -7
- package/package.json +1 -1
- package/src/__tests__/chirpier.test.ts +38 -16
- package/src/constants.ts +3 -3
- package/src/index.ts +31 -10
package/README.md
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
# Chirpier SDK
|
|
2
2
|
|
|
3
|
-
The Chirpier SDK is a
|
|
3
|
+
The Chirpier SDK for JavaScript is a simple, lightweight, and efficient SDK to emit event data to Chirpier direct from your JavaScript applications.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
+
- Easy-to-use API for sending events to Chirpier
|
|
8
|
+
- Automatic batching of events for improved performance
|
|
9
|
+
- Automatic retry mechanism with exponential backoff
|
|
10
|
+
- Thread-safe operations
|
|
11
|
+
- Periodic flushing of the event queue
|
|
7
12
|
- Environment Agnostic: Works seamlessly in both browser and Node.js environments.
|
|
8
|
-
- Retry Logic: Includes retry mechanisms with exponential backoff for failed requests.
|
|
9
|
-
- Offline Support: Queues events when offline and sends them when the connection is restored.
|
|
10
|
-
- Easy Integration: Simple API for quick integration into your projects.
|
|
11
13
|
|
|
12
14
|
## Installation
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
Install Chirpier SDK using npm:
|
|
15
17
|
|
|
16
|
-
```
|
|
18
|
+
``` bash
|
|
17
19
|
npm install @chirpier/chirpier-js
|
|
18
20
|
```
|
|
19
21
|
|
|
@@ -23,9 +25,11 @@ npm install @chirpier/chirpier-js
|
|
|
23
25
|
|
|
24
26
|
To start using the SDK, you need to initialize it with your API key. The SDK works in both browser and Node.js environments.
|
|
25
27
|
|
|
28
|
+
Here's a quick example of how to use the Chirpier SDK:
|
|
29
|
+
|
|
26
30
|
#### In a Browser
|
|
27
31
|
|
|
28
|
-
```
|
|
32
|
+
```js
|
|
29
33
|
import { initialize, monitor, Event } from '@chirpier/chirpier-js';
|
|
30
34
|
|
|
31
35
|
// Initialize the SDK with your API key
|
|
@@ -34,14 +38,14 @@ initialize({ key: 'your-api-key' });
|
|
|
34
38
|
// Send a data stream tied to a group of streams
|
|
35
39
|
monitor({
|
|
36
40
|
group_id: '02e4f4d8-415e-4fc1-b01a-677ac5bc9207',
|
|
37
|
-
stream_name: '
|
|
41
|
+
stream_name: 'My measurement',
|
|
38
42
|
value: 15.30,
|
|
39
43
|
} as Event);
|
|
40
44
|
```
|
|
41
45
|
|
|
42
46
|
#### In a Server (e.g., Express.js)
|
|
43
47
|
|
|
44
|
-
```
|
|
48
|
+
```js
|
|
45
49
|
const express = require('express');
|
|
46
50
|
const { initialize, monitor, Event } = require('@chirpier/chirpier-js');
|
|
47
51
|
|
|
@@ -49,7 +53,7 @@ const app = express();
|
|
|
49
53
|
const port = 3000;
|
|
50
54
|
|
|
51
55
|
// Initialize the SDK with your API key
|
|
52
|
-
initialize({ key: 'your-api-key' });
|
|
56
|
+
initialize({ key: 'your-api-key', region: 'us-west' });
|
|
53
57
|
|
|
54
58
|
app.use(express.json());
|
|
55
59
|
|
|
@@ -71,16 +75,81 @@ app.listen(port, () => {
|
|
|
71
75
|
});
|
|
72
76
|
```
|
|
73
77
|
|
|
74
|
-
|
|
78
|
+
### Usage
|
|
75
79
|
|
|
76
|
-
```
|
|
80
|
+
```js
|
|
77
81
|
// Initialize the SDK with your API key
|
|
78
|
-
initialize({ key: 'your-api-key' });
|
|
82
|
+
initialize({ key: 'your-api-key', region: 'us-west' });
|
|
79
83
|
|
|
80
84
|
// Monitor an event
|
|
81
85
|
monitor({
|
|
82
|
-
group_id: '
|
|
86
|
+
group_id: '02e4f4d8-415e-4fc1-b01a-677ac5bc9207',
|
|
83
87
|
stream_name: 'My measurement',
|
|
84
88
|
value: 15.3,
|
|
85
89
|
});
|
|
86
90
|
```
|
|
91
|
+
|
|
92
|
+
## API Reference
|
|
93
|
+
|
|
94
|
+
### Initialize
|
|
95
|
+
|
|
96
|
+
Initialize the Chirpier client with your API key and region. Find your API key in the Chirpier Integration page.
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
initialize({ key: 'your-api-key', region: 'region' });
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
- `your-api-key` (str): Your Chirpier integration key
|
|
103
|
+
- `region` (str): Your local region - options are `us-west`, `eu-west`, `asia-southeast`
|
|
104
|
+
|
|
105
|
+
### Event
|
|
106
|
+
|
|
107
|
+
All events emitted to Chirpier must have the following properties:
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
event = {
|
|
111
|
+
group_id: '02e4f4d8-415e-4fc1-b01a-677ac5bc9207',
|
|
112
|
+
stream_name: 'My measurement',
|
|
113
|
+
value: 15.3,
|
|
114
|
+
};
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- `group_id` (str): UUID of the monitoring group
|
|
118
|
+
- `stream_name` (str): Name of the measurement stream
|
|
119
|
+
- `value` (float): Numeric value to record
|
|
120
|
+
|
|
121
|
+
### Monitor
|
|
122
|
+
|
|
123
|
+
Send an event to Chirpier using the `monitor` function.
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
monitor(event);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Test
|
|
130
|
+
|
|
131
|
+
Run the test suite to ensure everything works as expected:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npx jest
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Contributing
|
|
138
|
+
|
|
139
|
+
We welcome contributions! To contribute:
|
|
140
|
+
|
|
141
|
+
1. Fork this repository.
|
|
142
|
+
2. Create a new branch for your feature or bug fix.
|
|
143
|
+
3. Submit a pull request with a clear explanation of your changes.
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
148
|
+
|
|
149
|
+
## Support
|
|
150
|
+
|
|
151
|
+
If you encounter any problems or have any questions, please open an issue on the GitHub repository or contact us at <contact@chirpier.co>.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
Start tracking your events seamlessly with Chirpier SDK!
|
|
@@ -57,9 +57,11 @@ describe("Chirpier SDK", function () {
|
|
|
57
57
|
test("should initialize with default values", function () {
|
|
58
58
|
(0, index_1.initialize)({
|
|
59
59
|
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
60
|
-
logLevel: index_1.LogLevel.None
|
|
60
|
+
logLevel: index_1.LogLevel.None,
|
|
61
|
+
});
|
|
62
|
+
var chirpier = index_1.Chirpier.getInstance({
|
|
63
|
+
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
61
64
|
});
|
|
62
|
-
var chirpier = index_1.Chirpier.getInstance({});
|
|
63
65
|
// Setup mock server
|
|
64
66
|
var mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
65
67
|
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
@@ -74,13 +76,13 @@ describe("Chirpier SDK", function () {
|
|
|
74
76
|
expect(function () {
|
|
75
77
|
(0, index_1.initialize)({
|
|
76
78
|
key: "api_key",
|
|
77
|
-
logLevel: index_1.LogLevel.None
|
|
79
|
+
logLevel: index_1.LogLevel.None,
|
|
78
80
|
});
|
|
79
81
|
}).toThrow(index_1.ChirpierError);
|
|
80
82
|
expect(function () {
|
|
81
83
|
(0, index_1.initialize)({
|
|
82
84
|
key: "api_key",
|
|
83
|
-
logLevel: index_1.LogLevel.None
|
|
85
|
+
logLevel: index_1.LogLevel.None,
|
|
84
86
|
});
|
|
85
87
|
}).toThrow("Invalid API key: Not a valid JWT");
|
|
86
88
|
});
|
|
@@ -95,7 +97,7 @@ describe("Chirpier SDK", function () {
|
|
|
95
97
|
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
96
98
|
(0, index_1.initialize)({
|
|
97
99
|
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
98
|
-
logLevel: index_1.LogLevel.None
|
|
100
|
+
logLevel: index_1.LogLevel.None,
|
|
99
101
|
});
|
|
100
102
|
event = {
|
|
101
103
|
group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
|
|
@@ -122,23 +124,31 @@ describe("Chirpier SDK", function () {
|
|
|
122
124
|
}
|
|
123
125
|
});
|
|
124
126
|
}); });
|
|
125
|
-
test("should
|
|
126
|
-
var
|
|
127
|
+
test("should silently drop invalid event", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
128
|
+
var mock, invalidEvent, consoleSpy;
|
|
127
129
|
return __generator(this, function (_a) {
|
|
128
130
|
switch (_a.label) {
|
|
129
131
|
case 0:
|
|
132
|
+
mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
133
|
+
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
130
134
|
(0, index_1.initialize)({
|
|
131
135
|
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
132
|
-
logLevel: index_1.LogLevel.
|
|
136
|
+
logLevel: index_1.LogLevel.Debug,
|
|
133
137
|
});
|
|
134
|
-
chirpier = index_1.Chirpier.getInstance({});
|
|
135
138
|
invalidEvent = {
|
|
136
|
-
group_id: "
|
|
139
|
+
group_id: "invalid-uuid",
|
|
140
|
+
stream_name: "",
|
|
141
|
+
value: 0,
|
|
137
142
|
};
|
|
138
|
-
|
|
143
|
+
consoleSpy = jest.spyOn(console, "debug");
|
|
144
|
+
return [4 /*yield*/, (0, index_1.monitor)(invalidEvent)];
|
|
139
145
|
case 1:
|
|
140
146
|
_a.sent();
|
|
141
|
-
|
|
147
|
+
expect(consoleSpy).toHaveBeenCalledWith("Invalid event format, dropping event:", invalidEvent);
|
|
148
|
+
expect(mock.history.post.length).toBe(0); // No request should be made
|
|
149
|
+
// Clean up
|
|
150
|
+
mock.reset();
|
|
151
|
+
consoleSpy.mockRestore();
|
|
142
152
|
index_1.Chirpier.stop();
|
|
143
153
|
return [2 /*return*/];
|
|
144
154
|
}
|
|
@@ -153,7 +163,7 @@ describe("Chirpier SDK", function () {
|
|
|
153
163
|
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
154
164
|
(0, index_1.initialize)({
|
|
155
165
|
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
156
|
-
logLevel: index_1.LogLevel.None
|
|
166
|
+
logLevel: index_1.LogLevel.None,
|
|
157
167
|
});
|
|
158
168
|
event = {
|
|
159
169
|
group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
|
|
@@ -182,7 +192,7 @@ describe("Chirpier SDK", function () {
|
|
|
182
192
|
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
183
193
|
(0, index_1.initialize)({
|
|
184
194
|
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
185
|
-
logLevel: index_1.LogLevel.None
|
|
195
|
+
logLevel: index_1.LogLevel.None,
|
|
186
196
|
});
|
|
187
197
|
event = {
|
|
188
198
|
group_id: "bfd9299d-817a-452f-bc53-6e154f2281fc",
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export declare const DEFAULT_API_ENDPOINT = "https://events.chirpier.co/v1.0/events";
|
|
2
1
|
export declare const DEFAULT_RETRIES = 15;
|
|
3
2
|
export declare const DEFAULT_TIMEOUT = 5000;
|
|
4
|
-
export declare const DEFAULT_BATCH_SIZE =
|
|
3
|
+
export declare const DEFAULT_BATCH_SIZE = 350;
|
|
5
4
|
export declare const DEFAULT_FLUSH_DELAY = 500;
|
|
6
|
-
export declare const MAX_QUEUE_SIZE =
|
|
5
|
+
export declare const MAX_QUEUE_SIZE = 50000;
|
|
6
|
+
export declare const DEFAULT_API_ENDPOINT = "https://eu-west.chirpier.co/v1.0/events";
|
|
7
7
|
//# 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,
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,eAAe,OAAO,CAAA;AACnC,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,cAAc,QAAQ,CAAC;AACpC,eAAO,MAAM,oBAAoB,4CAA4C,CAAA"}
|
package/dist/constants.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// constants.ts
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.MAX_QUEUE_SIZE = exports.DEFAULT_FLUSH_DELAY = exports.DEFAULT_BATCH_SIZE = exports.DEFAULT_TIMEOUT = exports.DEFAULT_RETRIES =
|
|
5
|
-
exports.DEFAULT_API_ENDPOINT = 'https://events.chirpier.co/v1.0/events';
|
|
4
|
+
exports.DEFAULT_API_ENDPOINT = exports.MAX_QUEUE_SIZE = exports.DEFAULT_FLUSH_DELAY = exports.DEFAULT_BATCH_SIZE = exports.DEFAULT_TIMEOUT = exports.DEFAULT_RETRIES = void 0;
|
|
6
5
|
exports.DEFAULT_RETRIES = 15;
|
|
7
6
|
exports.DEFAULT_TIMEOUT = 5000;
|
|
8
|
-
exports.DEFAULT_BATCH_SIZE =
|
|
7
|
+
exports.DEFAULT_BATCH_SIZE = 350;
|
|
9
8
|
exports.DEFAULT_FLUSH_DELAY = 500;
|
|
10
|
-
exports.MAX_QUEUE_SIZE =
|
|
9
|
+
exports.MAX_QUEUE_SIZE = 50000;
|
|
10
|
+
exports.DEFAULT_API_ENDPOINT = "https://eu-west.chirpier.co/v1.0/events";
|
package/dist/index.d.ts
CHANGED
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":"AAcA,oBAAY,QAAQ;IAClB,IAAI,IAAI;IACR,KAAK,IAAI;IACT,IAAI,IAAI;IACR,KAAK,IAAI;CACV;AAGD,UAAU,OAAO;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,gBAAgB,CAAC;IAClD,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,CAEvB;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAEvB;IACH,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IAEpC;;;OAGG;IACH,OAAO;IAyDP;;;;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;IA6BjD;;OAEG;YACW,UAAU;IA8DxB;;;OAGG;YACW,UAAU;WAKJ,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAa1C;AAsCD;;;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
|
@@ -101,14 +101,22 @@ var Chirpier = /** @class */ (function () {
|
|
|
101
101
|
function Chirpier(options) {
|
|
102
102
|
this.eventQueue = [];
|
|
103
103
|
this.flushTimeoutId = null;
|
|
104
|
-
this.queueLock = new async_lock_1.default(
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
this.queueLock = new async_lock_1.default({
|
|
105
|
+
maxPending: constants_1.MAX_QUEUE_SIZE,
|
|
106
|
+
});
|
|
107
|
+
this.flushLock = new async_lock_1.default({
|
|
108
|
+
maxPending: constants_1.MAX_QUEUE_SIZE,
|
|
109
|
+
});
|
|
110
|
+
var key = options.key, _a = options.region, region = _a === void 0 ? "eu-west" : _a, _b = options.logLevel, logLevel = _b === void 0 ? LogLevel.None : _b;
|
|
107
111
|
if (!key || typeof key !== "string") {
|
|
108
112
|
throw new ChirpierError("API key is required and must be a string");
|
|
109
113
|
}
|
|
114
|
+
if (typeof region !== "string" &&
|
|
115
|
+
!["us-west", "eu-west", "asia-southeast"].includes(region)) {
|
|
116
|
+
throw new ChirpierError("Region must be one of: us-west, eu-west, asia-southeast");
|
|
117
|
+
}
|
|
118
|
+
this.apiEndpoint = "https://".concat(region, ".chirpier.co/v1.0/events");
|
|
110
119
|
this.apiKey = key;
|
|
111
|
-
this.apiEndpoint = constants_1.DEFAULT_API_ENDPOINT;
|
|
112
120
|
this.retries = constants_1.DEFAULT_RETRIES;
|
|
113
121
|
this.timeout = constants_1.DEFAULT_TIMEOUT;
|
|
114
122
|
this.batchSize = constants_1.DEFAULT_BATCH_SIZE;
|
|
@@ -131,7 +139,10 @@ var Chirpier = /** @class */ (function () {
|
|
|
131
139
|
return Math.pow(2, retryCount) * 1000; // Exponential backoff starting at 1 second
|
|
132
140
|
},
|
|
133
141
|
retryCondition: function (error) {
|
|
134
|
-
|
|
142
|
+
// Retry on network errors, 5xx errors, and 429 (Too Many Requests)
|
|
143
|
+
return (axios_retry_1.default.isNetworkError(error) ||
|
|
144
|
+
axios_retry_1.default.isRetryableError(error) ||
|
|
145
|
+
(error.response && error.response.status) === 429);
|
|
135
146
|
},
|
|
136
147
|
shouldResetTimeout: true,
|
|
137
148
|
});
|
|
@@ -171,12 +182,18 @@ var Chirpier = /** @class */ (function () {
|
|
|
171
182
|
switch (_a.label) {
|
|
172
183
|
case 0:
|
|
173
184
|
if (!this.isValidEvent(event)) {
|
|
174
|
-
|
|
185
|
+
if (this.logLevel >= LogLevel.Debug) {
|
|
186
|
+
console.debug("Invalid event format, dropping event:", event);
|
|
187
|
+
}
|
|
188
|
+
return [2 /*return*/]; // Silently drop the event
|
|
175
189
|
}
|
|
176
190
|
return [4 /*yield*/, this.queueLock.acquire("queue", function () { return __awaiter(_this, void 0, void 0, function () {
|
|
177
191
|
return __generator(this, function (_a) {
|
|
178
192
|
if (this.eventQueue.length >= constants_1.MAX_QUEUE_SIZE) {
|
|
179
|
-
|
|
193
|
+
if (this.logLevel >= LogLevel.Debug) {
|
|
194
|
+
console.debug("Event queue is full, dropping event:", event);
|
|
195
|
+
}
|
|
196
|
+
return [2 /*return*/]; // Silently drop the event
|
|
180
197
|
}
|
|
181
198
|
this.eventQueue.push({ event: event, timestamp: Date.now(), retryCount: 0 });
|
|
182
199
|
return [2 /*return*/];
|
|
@@ -367,6 +384,7 @@ function isValidJWT(token) {
|
|
|
367
384
|
return typeof header === "object" && typeof payload === "object";
|
|
368
385
|
}
|
|
369
386
|
catch (error) {
|
|
387
|
+
console.error("Failed to validate JWT:", error);
|
|
370
388
|
return false;
|
|
371
389
|
}
|
|
372
390
|
}
|
package/package.json
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Chirpier,
|
|
3
|
+
ChirpierError,
|
|
4
|
+
Event,
|
|
5
|
+
LogLevel,
|
|
6
|
+
initialize,
|
|
7
|
+
monitor,
|
|
8
|
+
} from "../index";
|
|
2
9
|
import {
|
|
3
10
|
DEFAULT_API_ENDPOINT,
|
|
4
11
|
DEFAULT_RETRIES,
|
|
@@ -27,9 +34,11 @@ describe("Chirpier SDK", () => {
|
|
|
27
34
|
test("should initialize with default values", () => {
|
|
28
35
|
initialize({
|
|
29
36
|
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
30
|
-
logLevel: LogLevel.None
|
|
37
|
+
logLevel: LogLevel.None,
|
|
38
|
+
});
|
|
39
|
+
const chirpier = Chirpier.getInstance({
|
|
40
|
+
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
31
41
|
});
|
|
32
|
-
const chirpier = Chirpier.getInstance({} as any);
|
|
33
42
|
|
|
34
43
|
// Setup mock server
|
|
35
44
|
const mock = new MockAdapter(axios);
|
|
@@ -48,13 +57,13 @@ describe("Chirpier SDK", () => {
|
|
|
48
57
|
expect(() => {
|
|
49
58
|
initialize({
|
|
50
59
|
key: "api_key",
|
|
51
|
-
logLevel: LogLevel.None
|
|
60
|
+
logLevel: LogLevel.None,
|
|
52
61
|
});
|
|
53
62
|
}).toThrow(ChirpierError);
|
|
54
63
|
expect(() => {
|
|
55
64
|
initialize({
|
|
56
65
|
key: "api_key",
|
|
57
|
-
logLevel: LogLevel.None
|
|
66
|
+
logLevel: LogLevel.None,
|
|
58
67
|
});
|
|
59
68
|
}).toThrow("Invalid API key: Not a valid JWT");
|
|
60
69
|
});
|
|
@@ -68,7 +77,7 @@ describe("Chirpier SDK", () => {
|
|
|
68
77
|
|
|
69
78
|
initialize({
|
|
70
79
|
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
71
|
-
logLevel: LogLevel.None
|
|
80
|
+
logLevel: LogLevel.None,
|
|
72
81
|
});
|
|
73
82
|
|
|
74
83
|
const event: Event = {
|
|
@@ -96,21 +105,34 @@ describe("Chirpier SDK", () => {
|
|
|
96
105
|
Chirpier.stop();
|
|
97
106
|
});
|
|
98
107
|
|
|
99
|
-
test("should
|
|
108
|
+
test("should silently drop invalid event", async () => {
|
|
109
|
+
const mock = new MockAdapter(axios);
|
|
110
|
+
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
111
|
+
|
|
100
112
|
initialize({
|
|
101
113
|
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
102
|
-
logLevel: LogLevel.
|
|
114
|
+
logLevel: LogLevel.Debug,
|
|
103
115
|
});
|
|
104
|
-
const chirpier = Chirpier.getInstance({} as any);
|
|
105
116
|
|
|
106
117
|
const invalidEvent = {
|
|
107
|
-
group_id: "
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
118
|
+
group_id: "invalid-uuid",
|
|
119
|
+
stream_name: "",
|
|
120
|
+
value: 0,
|
|
121
|
+
} as Event;
|
|
122
|
+
|
|
123
|
+
const consoleSpy = jest.spyOn(console, "debug");
|
|
124
|
+
|
|
125
|
+
await monitor(invalidEvent);
|
|
126
|
+
|
|
127
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
128
|
+
"Invalid event format, dropping event:",
|
|
129
|
+
invalidEvent
|
|
111
130
|
);
|
|
131
|
+
expect(mock.history.post.length).toBe(0); // No request should be made
|
|
112
132
|
|
|
113
|
-
// Clean up
|
|
133
|
+
// Clean up
|
|
134
|
+
mock.reset();
|
|
135
|
+
consoleSpy.mockRestore();
|
|
114
136
|
Chirpier.stop();
|
|
115
137
|
});
|
|
116
138
|
|
|
@@ -120,7 +142,7 @@ describe("Chirpier SDK", () => {
|
|
|
120
142
|
|
|
121
143
|
initialize({
|
|
122
144
|
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
123
|
-
logLevel: LogLevel.None
|
|
145
|
+
logLevel: LogLevel.None,
|
|
124
146
|
});
|
|
125
147
|
|
|
126
148
|
const event: Event = {
|
|
@@ -147,7 +169,7 @@ describe("Chirpier SDK", () => {
|
|
|
147
169
|
|
|
148
170
|
initialize({
|
|
149
171
|
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
|
150
|
-
logLevel: LogLevel.None
|
|
172
|
+
logLevel: LogLevel.None,
|
|
151
173
|
});
|
|
152
174
|
|
|
153
175
|
const event: Event = {
|
package/src/constants.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// constants.ts
|
|
2
2
|
|
|
3
|
-
export const DEFAULT_API_ENDPOINT = 'https://events.chirpier.co/v1.0/events';
|
|
4
3
|
export const DEFAULT_RETRIES = 15;
|
|
5
4
|
export const DEFAULT_TIMEOUT = 5000
|
|
6
|
-
export const DEFAULT_BATCH_SIZE =
|
|
5
|
+
export const DEFAULT_BATCH_SIZE = 350;
|
|
7
6
|
export const DEFAULT_FLUSH_DELAY = 500;
|
|
8
|
-
export const MAX_QUEUE_SIZE =
|
|
7
|
+
export const MAX_QUEUE_SIZE = 50000;
|
|
8
|
+
export const DEFAULT_API_ENDPOINT = "https://eu-west.chirpier.co/v1.0/events"
|
package/src/index.ts
CHANGED
|
@@ -3,7 +3,6 @@ import axios, { AxiosInstance } from "axios";
|
|
|
3
3
|
import axiosRetry from "axios-retry";
|
|
4
4
|
import { Base64 } from "js-base64";
|
|
5
5
|
import {
|
|
6
|
-
DEFAULT_API_ENDPOINT,
|
|
7
6
|
DEFAULT_RETRIES,
|
|
8
7
|
DEFAULT_TIMEOUT,
|
|
9
8
|
DEFAULT_BATCH_SIZE,
|
|
@@ -23,6 +22,7 @@ export enum LogLevel {
|
|
|
23
22
|
// Define the options interface for Chirpier initialization
|
|
24
23
|
interface Options {
|
|
25
24
|
key: string;
|
|
25
|
+
region?: "us-west" | "eu-west" | "asia-southeast";
|
|
26
26
|
logLevel?: LogLevel;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -62,8 +62,12 @@ export class Chirpier {
|
|
|
62
62
|
private readonly batchSize: number;
|
|
63
63
|
private readonly flushDelay: number;
|
|
64
64
|
private flushTimeoutId: NodeJS.Timeout | null = null;
|
|
65
|
-
private readonly queueLock = new AsyncLock(
|
|
66
|
-
|
|
65
|
+
private readonly queueLock = new AsyncLock({
|
|
66
|
+
maxPending: MAX_QUEUE_SIZE,
|
|
67
|
+
});
|
|
68
|
+
private readonly flushLock = new AsyncLock({
|
|
69
|
+
maxPending: MAX_QUEUE_SIZE,
|
|
70
|
+
});
|
|
67
71
|
private readonly logLevel: LogLevel;
|
|
68
72
|
|
|
69
73
|
/**
|
|
@@ -71,14 +75,23 @@ export class Chirpier {
|
|
|
71
75
|
* @param options - Configuration options for the SDK.
|
|
72
76
|
*/
|
|
73
77
|
private constructor(options: Options) {
|
|
74
|
-
const { key, logLevel = LogLevel.None } = options;
|
|
78
|
+
const { key, region = "eu-west", logLevel = LogLevel.None } = options;
|
|
75
79
|
|
|
76
80
|
if (!key || typeof key !== "string") {
|
|
77
81
|
throw new ChirpierError("API key is required and must be a string");
|
|
78
82
|
}
|
|
79
83
|
|
|
84
|
+
if (
|
|
85
|
+
typeof region !== "string" &&
|
|
86
|
+
!["us-west", "eu-west", "asia-southeast"].includes(region)
|
|
87
|
+
) {
|
|
88
|
+
throw new ChirpierError(
|
|
89
|
+
"Region must be one of: us-west, eu-west, asia-southeast"
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this.apiEndpoint = `https://${region}.chirpier.co/v1.0/events`;
|
|
80
94
|
this.apiKey = key;
|
|
81
|
-
this.apiEndpoint = DEFAULT_API_ENDPOINT;
|
|
82
95
|
this.retries = DEFAULT_RETRIES;
|
|
83
96
|
this.timeout = DEFAULT_TIMEOUT;
|
|
84
97
|
this.batchSize = DEFAULT_BATCH_SIZE;
|
|
@@ -107,8 +120,11 @@ export class Chirpier {
|
|
|
107
120
|
return Math.pow(2, retryCount) * 1000; // Exponential backoff starting at 1 second
|
|
108
121
|
},
|
|
109
122
|
retryCondition: (error) => {
|
|
123
|
+
// Retry on network errors, 5xx errors, and 429 (Too Many Requests)
|
|
110
124
|
return (
|
|
111
|
-
axiosRetry.isNetworkError(error) ||
|
|
125
|
+
axiosRetry.isNetworkError(error) ||
|
|
126
|
+
axiosRetry.isRetryableError(error) ||
|
|
127
|
+
(error.response && error.response.status) === 429
|
|
112
128
|
);
|
|
113
129
|
},
|
|
114
130
|
shouldResetTimeout: true,
|
|
@@ -151,14 +167,18 @@ export class Chirpier {
|
|
|
151
167
|
*/
|
|
152
168
|
public async monitor(event: Event): Promise<void> {
|
|
153
169
|
if (!this.isValidEvent(event)) {
|
|
154
|
-
|
|
155
|
-
"Invalid event format
|
|
156
|
-
|
|
170
|
+
if (this.logLevel >= LogLevel.Debug) {
|
|
171
|
+
console.debug("Invalid event format, dropping event:", event);
|
|
172
|
+
}
|
|
173
|
+
return; // Silently drop the event
|
|
157
174
|
}
|
|
158
175
|
|
|
159
176
|
await this.queueLock.acquire("queue", async () => {
|
|
160
177
|
if (this.eventQueue.length >= MAX_QUEUE_SIZE) {
|
|
161
|
-
|
|
178
|
+
if (this.logLevel >= LogLevel.Debug) {
|
|
179
|
+
console.debug("Event queue is full, dropping event:", event);
|
|
180
|
+
}
|
|
181
|
+
return; // Silently drop the event
|
|
162
182
|
}
|
|
163
183
|
|
|
164
184
|
this.eventQueue.push({ event, timestamp: Date.now(), retryCount: 0 });
|
|
@@ -294,6 +314,7 @@ function isValidJWT(token: string): boolean {
|
|
|
294
314
|
const payload = JSON.parse(base64UrlDecode(parts[1]));
|
|
295
315
|
return typeof header === "object" && typeof payload === "object";
|
|
296
316
|
} catch (error) {
|
|
317
|
+
console.error("Failed to validate JWT:", error);
|
|
297
318
|
return false;
|
|
298
319
|
}
|
|
299
320
|
}
|