@chirpier/chirpier-js 0.2.0 → 0.2.1
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 +12 -8
- package/dist/__tests__/chirpier.test.js +44 -8
- package/dist/constants.d.ts.map +1 -1
- package/dist/index.d.ts +11 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +52 -52
- package/package.json +4 -4
- package/src/__tests__/chirpier.test.ts +38 -6
- package/src/index.ts +55 -66
package/README.md
CHANGED
|
@@ -21,7 +21,8 @@ import { initialize, logEvent, stop } from "@chirpier/chirpier-js";
|
|
|
21
21
|
initialize({ key: "chp_your_api_key" });
|
|
22
22
|
|
|
23
23
|
await logEvent({
|
|
24
|
-
|
|
24
|
+
log_id: "9f97d65f-fb30-4062-b4d0-8617c03fe4f6",
|
|
25
|
+
agent: "openclaw.main",
|
|
25
26
|
event: "tool.errors.count",
|
|
26
27
|
value: 1,
|
|
27
28
|
meta: { tool_name: "browser.open", workflow: "triage" },
|
|
@@ -38,7 +39,7 @@ import { createClient } from "@chirpier/chirpier-js";
|
|
|
38
39
|
const client = createClient({ key: "chp_your_api_key" });
|
|
39
40
|
|
|
40
41
|
await client.log({
|
|
41
|
-
|
|
42
|
+
agent: "openclaw.main",
|
|
42
43
|
event: "task.duration_ms",
|
|
43
44
|
value: 420,
|
|
44
45
|
meta: { task_name: "email_triage", result: "success" },
|
|
@@ -64,7 +65,7 @@ Initializes the SDK singleton.
|
|
|
64
65
|
- `timeout` (number, optional): HTTP timeout in ms.
|
|
65
66
|
- `batchSize` (number, optional): Flush size threshold.
|
|
66
67
|
- `flushDelay` (number, optional): Flush interval in ms.
|
|
67
|
-
- `maxQueueSize` (number, optional):
|
|
68
|
+
- `maxQueueSize` (number, optional, deprecated): Ignored; queues grow in memory until flushed.
|
|
68
69
|
|
|
69
70
|
API key resolution precedence (when `key` is omitted):
|
|
70
71
|
1. `CHIRPIER_API_KEY` from process environment
|
|
@@ -73,6 +74,7 @@ API key resolution precedence (when `key` is omitted):
|
|
|
73
74
|
Default ingest endpoint is `https://logs.chirpier.co/v1.0/logs`.
|
|
74
75
|
Default servicer endpoint is `https://api.chirpier.co/v1.0`.
|
|
75
76
|
The same bearer token is used for both ingest and servicer APIs.
|
|
77
|
+
Queued logs are not dropped locally because of queue capacity or retry exhaustion.
|
|
76
78
|
|
|
77
79
|
### `logEvent(log)`
|
|
78
80
|
|
|
@@ -82,7 +84,7 @@ Example with `occurred_at`:
|
|
|
82
84
|
|
|
83
85
|
```ts
|
|
84
86
|
await logEvent({
|
|
85
|
-
|
|
87
|
+
agent: "openclaw.main",
|
|
86
88
|
event: "tokens.used",
|
|
87
89
|
value: 1530,
|
|
88
90
|
occurred_at: "2026-03-05T14:30:00Z",
|
|
@@ -90,14 +92,16 @@ await logEvent({
|
|
|
90
92
|
```
|
|
91
93
|
|
|
92
94
|
`log`:
|
|
93
|
-
- `
|
|
95
|
+
- `agent` (string, optional): Free-form agent identifier text.
|
|
96
|
+
- `log_id` (string, optional): UUID idempotency key for the log. Generated automatically when omitted.
|
|
94
97
|
- `event` (string, required): Event name.
|
|
95
98
|
- `value` (number, required): Numeric value.
|
|
96
99
|
- `occurred_at` (string | Date, optional): Event occurrence timestamp.
|
|
97
100
|
- `meta` (JSON, optional): Additional JSON-encodable metadata.
|
|
98
101
|
|
|
99
102
|
Notes:
|
|
100
|
-
- `
|
|
103
|
+
- `agent` whitespace-only values are treated as omitted.
|
|
104
|
+
- `log_id` blank values are treated as omitted and replaced with a generated UUIDv4.
|
|
101
105
|
- `event` must be non-empty after trimming.
|
|
102
106
|
- `occurred_at` must be within the last 30 days and no more than 1 day in the future.
|
|
103
107
|
- Use ISO8601 UTC timestamps, such as `2026-03-05T14:30:00Z`, or pass a `Date` instance.
|
|
@@ -138,14 +142,14 @@ Client methods:
|
|
|
138
142
|
const client = createClient({ key: "chp_your_api_key" });
|
|
139
143
|
|
|
140
144
|
await client.log({
|
|
141
|
-
|
|
145
|
+
agent: "openclaw.main",
|
|
142
146
|
event: "tool.errors.count",
|
|
143
147
|
value: 1,
|
|
144
148
|
meta: { tool_name: "browser.open" },
|
|
145
149
|
});
|
|
146
150
|
|
|
147
151
|
await client.log({
|
|
148
|
-
|
|
152
|
+
agent: "openclaw.main",
|
|
149
153
|
event: "task.duration_ms",
|
|
150
154
|
value: 780,
|
|
151
155
|
meta: { task_name: "daily_digest" },
|
|
@@ -9,8 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
-
return g =
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
13
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
14
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
15
|
function step(op) {
|
|
16
16
|
if (f) throw new TypeError("Generator is already executing.");
|
|
@@ -190,7 +190,7 @@ describe("Chirpier SDK", function () {
|
|
|
190
190
|
logLevel: 0 /* LogLevel.None */,
|
|
191
191
|
});
|
|
192
192
|
log = {
|
|
193
|
-
|
|
193
|
+
agent: "api.worker",
|
|
194
194
|
event: "request.finished",
|
|
195
195
|
value: 1,
|
|
196
196
|
};
|
|
@@ -204,16 +204,45 @@ describe("Chirpier SDK", function () {
|
|
|
204
204
|
expect(mock.history.post[0].url).toBe(constants_1.DEFAULT_API_ENDPOINT);
|
|
205
205
|
expect(JSON.parse(mock.history.post[0].data)).toEqual([
|
|
206
206
|
{
|
|
207
|
-
|
|
207
|
+
log_id: expect.any(String),
|
|
208
|
+
agent: "api.worker",
|
|
208
209
|
event: "request.finished",
|
|
209
210
|
value: 1,
|
|
210
211
|
},
|
|
211
212
|
]);
|
|
213
|
+
expect(JSON.parse(mock.history.post[0].data)[0].log_id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
|
|
212
214
|
return [2 /*return*/];
|
|
213
215
|
}
|
|
214
216
|
});
|
|
215
217
|
}); });
|
|
216
|
-
test("
|
|
218
|
+
test("should preserve provided log_id", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
219
|
+
var mock, payload;
|
|
220
|
+
return __generator(this, function (_a) {
|
|
221
|
+
switch (_a.label) {
|
|
222
|
+
case 0:
|
|
223
|
+
mock = new axios_mock_adapter_1.default(axios_1.default);
|
|
224
|
+
mock.onPost(constants_1.DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
225
|
+
(0, index_1.initialize)({
|
|
226
|
+
key: "chp_log_id_key",
|
|
227
|
+
logLevel: 0 /* LogLevel.None */,
|
|
228
|
+
});
|
|
229
|
+
return [4 /*yield*/, (0, index_1.logEvent)({
|
|
230
|
+
log_id: "9f97d65f-fb30-4062-b4d0-8617c03fe4f6",
|
|
231
|
+
event: "request.finished",
|
|
232
|
+
value: 1,
|
|
233
|
+
})];
|
|
234
|
+
case 1:
|
|
235
|
+
_a.sent();
|
|
236
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })];
|
|
237
|
+
case 2:
|
|
238
|
+
_a.sent();
|
|
239
|
+
payload = JSON.parse(mock.history.post[0].data);
|
|
240
|
+
expect(payload[0].log_id).toBe("9f97d65f-fb30-4062-b4d0-8617c03fe4f6");
|
|
241
|
+
return [2 /*return*/];
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}); });
|
|
245
|
+
test("agent whitespace should be omitted", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
217
246
|
var mock, payload;
|
|
218
247
|
return __generator(this, function (_a) {
|
|
219
248
|
switch (_a.label) {
|
|
@@ -225,7 +254,7 @@ describe("Chirpier SDK", function () {
|
|
|
225
254
|
logLevel: 0 /* LogLevel.None */,
|
|
226
255
|
});
|
|
227
256
|
return [4 /*yield*/, (0, index_1.logEvent)({
|
|
228
|
-
|
|
257
|
+
agent: " ",
|
|
229
258
|
event: "metric.tick",
|
|
230
259
|
value: 42,
|
|
231
260
|
})];
|
|
@@ -235,7 +264,7 @@ describe("Chirpier SDK", function () {
|
|
|
235
264
|
case 2:
|
|
236
265
|
_a.sent();
|
|
237
266
|
payload = JSON.parse(mock.history.post[0].data);
|
|
238
|
-
expect(payload[0].
|
|
267
|
+
expect(payload[0].agent).toBeUndefined();
|
|
239
268
|
return [2 /*return*/];
|
|
240
269
|
}
|
|
241
270
|
});
|
|
@@ -252,7 +281,7 @@ describe("Chirpier SDK", function () {
|
|
|
252
281
|
logLevel: 0 /* LogLevel.None */,
|
|
253
282
|
});
|
|
254
283
|
return [4 /*yield*/, (0, index_1.logEvent)({
|
|
255
|
-
|
|
284
|
+
agent: "api.worker",
|
|
256
285
|
event: "request.finished",
|
|
257
286
|
value: 200,
|
|
258
287
|
meta: {
|
|
@@ -329,6 +358,13 @@ describe("Chirpier SDK", function () {
|
|
|
329
358
|
occurred_at: new Date(Date.now() + 25 * 60 * 60 * 1000),
|
|
330
359
|
})).rejects.toThrow(index_1.ChirpierError)];
|
|
331
360
|
case 3:
|
|
361
|
+
_a.sent();
|
|
362
|
+
return [4 /*yield*/, expect((0, index_1.logEvent)({
|
|
363
|
+
log_id: "not-a-uuid",
|
|
364
|
+
event: "bad-log-id",
|
|
365
|
+
value: 1,
|
|
366
|
+
})).rejects.toThrow(index_1.ChirpierError)];
|
|
367
|
+
case 4:
|
|
332
368
|
_a.sent();
|
|
333
369
|
expect(mock.history.post.length).toBe(0);
|
|
334
370
|
return [2 /*return*/];
|
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,eAAe,
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe,EAAG,EAAW,CAAC;AAC3C,eAAO,MAAM,eAAe,EAAG,IAAa,CAAC;AAC7C,eAAO,MAAM,kBAAkB,EAAG,GAAY,CAAC;AAC/C,eAAO,MAAM,mBAAmB,EAAG,GAAY,CAAC;AAChD,eAAO,MAAM,cAAc,EAAG,IAAa,CAAC;AAC5C,eAAO,MAAM,oBAAoB,EAAG,oCAA6C,CAAC;AAClF,eAAO,MAAM,yBAAyB,EAAG,8BAAuC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface Config {
|
|
|
13
13
|
timeout?: number;
|
|
14
14
|
batchSize?: number;
|
|
15
15
|
flushDelay?: number;
|
|
16
|
+
/** @deprecated Queues are unbounded in memory. */
|
|
16
17
|
maxQueueSize?: number;
|
|
17
18
|
}
|
|
18
19
|
/**
|
|
@@ -20,25 +21,22 @@ export interface Config {
|
|
|
20
21
|
*/
|
|
21
22
|
export type Options = Config;
|
|
22
23
|
export interface Log {
|
|
23
|
-
|
|
24
|
+
log_id?: string;
|
|
25
|
+
agent?: string;
|
|
24
26
|
event: string;
|
|
25
27
|
value: number;
|
|
26
28
|
meta?: unknown;
|
|
27
29
|
occurred_at?: string | Date;
|
|
28
30
|
}
|
|
29
|
-
export interface
|
|
31
|
+
export interface Event {
|
|
30
32
|
readonly event_id: string;
|
|
31
|
-
readonly
|
|
33
|
+
readonly agent?: string;
|
|
32
34
|
readonly event: string;
|
|
33
35
|
readonly title?: string;
|
|
34
36
|
readonly public: boolean;
|
|
35
37
|
readonly description?: string;
|
|
36
38
|
readonly unit?: string;
|
|
37
|
-
readonly
|
|
38
|
-
readonly default_aggregate: string;
|
|
39
|
-
readonly enabled: boolean;
|
|
40
|
-
readonly origin: string;
|
|
41
|
-
readonly archived_at?: string;
|
|
39
|
+
readonly timezone: string;
|
|
42
40
|
readonly created_at?: string;
|
|
43
41
|
}
|
|
44
42
|
export interface Policy {
|
|
@@ -58,7 +56,7 @@ export interface Alert {
|
|
|
58
56
|
readonly alert_id: string;
|
|
59
57
|
readonly policy_id: string;
|
|
60
58
|
readonly event_id: string;
|
|
61
|
-
readonly
|
|
59
|
+
readonly agent?: string;
|
|
62
60
|
readonly event: string;
|
|
63
61
|
readonly title: string;
|
|
64
62
|
readonly period: string;
|
|
@@ -88,7 +86,7 @@ export interface AlertDelivery {
|
|
|
88
86
|
}
|
|
89
87
|
export interface EventLogPoint {
|
|
90
88
|
readonly event_id: string;
|
|
91
|
-
readonly
|
|
89
|
+
readonly agent?: string;
|
|
92
90
|
readonly event: string;
|
|
93
91
|
readonly period: string;
|
|
94
92
|
readonly occurred_at: string;
|
|
@@ -118,7 +116,6 @@ export declare class Client {
|
|
|
118
116
|
private logQueue;
|
|
119
117
|
private readonly batchSize;
|
|
120
118
|
private readonly flushDelay;
|
|
121
|
-
private readonly maxQueueSize;
|
|
122
119
|
private flushTimeoutId;
|
|
123
120
|
private readonly queueLock;
|
|
124
121
|
private readonly flushLock;
|
|
@@ -132,9 +129,9 @@ export declare class Client {
|
|
|
132
129
|
flush(): Promise<void>;
|
|
133
130
|
shutdown(): Promise<void>;
|
|
134
131
|
close(): Promise<void>;
|
|
135
|
-
listEvents(): Promise<
|
|
136
|
-
getEvent(eventID: string): Promise<
|
|
137
|
-
updateEvent(eventID: string, payload: Partial<Omit<
|
|
132
|
+
listEvents(): Promise<Event[]>;
|
|
133
|
+
getEvent(eventID: string): Promise<Event>;
|
|
134
|
+
updateEvent(eventID: string, payload: Partial<Omit<Event, "event_id" | "created_at">>): Promise<Event>;
|
|
138
135
|
listPolicies(): Promise<Policy[]>;
|
|
139
136
|
createPolicy(payload: Omit<Policy, "policy_id">): Promise<Policy>;
|
|
140
137
|
listAlerts(status?: string): Promise<Alert[]>;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,0BAAkB,QAAQ;IACxB,IAAI,IAAI;IACR,KAAK,IAAI;IACT,IAAI,IAAI;IACR,KAAK,IAAI;CACV;AAED,MAAM,WAAW,MAAM;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B,MAAM,WAAW,GAAG;IAClB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -35,8 +35,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
35
35
|
});
|
|
36
36
|
};
|
|
37
37
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
38
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
39
|
-
return g =
|
|
38
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
39
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
40
40
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
41
41
|
function step(op) {
|
|
42
42
|
if (f) throw new TypeError("Generator is already executing.");
|
|
@@ -74,12 +74,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
74
74
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
75
75
|
};
|
|
76
76
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
77
|
-
exports.
|
|
77
|
+
exports.Client = exports.ChirpierError = void 0;
|
|
78
|
+
exports.createClient = createClient;
|
|
79
|
+
exports.initialize = initialize;
|
|
80
|
+
exports.logEvent = logEvent;
|
|
81
|
+
exports.stop = stop;
|
|
82
|
+
exports.flush = flush;
|
|
78
83
|
var axios_1 = __importDefault(require("axios"));
|
|
79
84
|
var axios_retry_1 = __importDefault(require("axios-retry"));
|
|
80
85
|
var dotenv_1 = __importDefault(require("dotenv"));
|
|
81
86
|
var constants_1 = require("./constants");
|
|
82
87
|
var async_lock_1 = __importDefault(require("async-lock"));
|
|
88
|
+
var uuid_1 = require("uuid");
|
|
83
89
|
// Custom error class for Chirpier-specific errors
|
|
84
90
|
var ChirpierError = /** @class */ (function (_super) {
|
|
85
91
|
__extends(ChirpierError, _super);
|
|
@@ -98,7 +104,7 @@ var Client = /** @class */ (function () {
|
|
|
98
104
|
if (options === void 0) { options = {}; }
|
|
99
105
|
this.logQueue = [];
|
|
100
106
|
this.flushTimeoutId = null;
|
|
101
|
-
var providedKey = options.key, _a = options.apiEndpoint, apiEndpoint = _a === void 0 ? constants_1.DEFAULT_API_ENDPOINT : _a, _b = options.servicerEndpoint, servicerEndpoint = _b === void 0 ? constants_1.DEFAULT_SERVICER_ENDPOINT : _b, _c = options.logLevel, logLevel = _c === void 0 ? 0 /* LogLevel.None */ : _c, _d = options.retries, retries = _d === void 0 ? constants_1.DEFAULT_RETRIES : _d, _e = options.timeout, timeout = _e === void 0 ? constants_1.DEFAULT_TIMEOUT : _e, _f = options.batchSize, batchSize = _f === void 0 ? constants_1.DEFAULT_BATCH_SIZE : _f, _g = options.flushDelay, flushDelay = _g === void 0 ? constants_1.DEFAULT_FLUSH_DELAY : _g,
|
|
107
|
+
var providedKey = options.key, _a = options.apiEndpoint, apiEndpoint = _a === void 0 ? constants_1.DEFAULT_API_ENDPOINT : _a, _b = options.servicerEndpoint, servicerEndpoint = _b === void 0 ? constants_1.DEFAULT_SERVICER_ENDPOINT : _b, _c = options.logLevel, logLevel = _c === void 0 ? 0 /* LogLevel.None */ : _c, _d = options.retries, retries = _d === void 0 ? constants_1.DEFAULT_RETRIES : _d, _e = options.timeout, timeout = _e === void 0 ? constants_1.DEFAULT_TIMEOUT : _e, _f = options.batchSize, batchSize = _f === void 0 ? constants_1.DEFAULT_BATCH_SIZE : _f, _g = options.flushDelay, flushDelay = _g === void 0 ? constants_1.DEFAULT_FLUSH_DELAY : _g, maxQueueSize = options.maxQueueSize;
|
|
102
108
|
var key = resolveAPIKey(providedKey);
|
|
103
109
|
if (!key) {
|
|
104
110
|
throw new ChirpierError("API key is required", "INVALID_KEY");
|
|
@@ -114,7 +120,7 @@ var Client = /** @class */ (function () {
|
|
|
114
120
|
try {
|
|
115
121
|
parsedURL = new URL(apiEndpoint);
|
|
116
122
|
}
|
|
117
|
-
catch (
|
|
123
|
+
catch (_h) {
|
|
118
124
|
throw new ChirpierError("apiEndpoint must be a valid absolute URL", "INVALID_API_ENDPOINT");
|
|
119
125
|
}
|
|
120
126
|
if (parsedURL.protocol !== "https:" && parsedURL.protocol !== "http:") {
|
|
@@ -134,9 +140,6 @@ var Client = /** @class */ (function () {
|
|
|
134
140
|
if (flushDelay < 0) {
|
|
135
141
|
throw new ChirpierError("Flush delay must be non-negative", "INVALID_FLUSH_DELAY");
|
|
136
142
|
}
|
|
137
|
-
if (maxQueueSize <= 0 || !Number.isInteger(maxQueueSize)) {
|
|
138
|
-
throw new ChirpierError("Max queue size must be a positive integer", "INVALID_QUEUE_SIZE");
|
|
139
|
-
}
|
|
140
143
|
this.apiEndpoint = apiEndpoint !== null && apiEndpoint !== void 0 ? apiEndpoint : constants_1.DEFAULT_API_ENDPOINT;
|
|
141
144
|
this.servicerEndpoint = servicerEndpoint !== null && servicerEndpoint !== void 0 ? servicerEndpoint : constants_1.DEFAULT_SERVICER_ENDPOINT;
|
|
142
145
|
this.apiKey = key;
|
|
@@ -144,10 +147,10 @@ var Client = /** @class */ (function () {
|
|
|
144
147
|
this.timeout = timeout;
|
|
145
148
|
this.batchSize = batchSize;
|
|
146
149
|
this.flushDelay = flushDelay;
|
|
147
|
-
this.maxQueueSize = maxQueueSize;
|
|
148
150
|
this.logLevel = logLevel;
|
|
149
|
-
|
|
150
|
-
this.
|
|
151
|
+
void maxQueueSize;
|
|
152
|
+
this.queueLock = new async_lock_1.default();
|
|
153
|
+
this.flushLock = new async_lock_1.default();
|
|
151
154
|
this.axiosInstance = axios_1.default.create({
|
|
152
155
|
headers: { Authorization: "Bearer ".concat(this.apiKey) },
|
|
153
156
|
timeout: this.timeout,
|
|
@@ -175,10 +178,19 @@ var Client = /** @class */ (function () {
|
|
|
175
178
|
if (typeof log.event !== "string" || log.event.trim().length === 0) {
|
|
176
179
|
return false;
|
|
177
180
|
}
|
|
181
|
+
if (log.log_id !== undefined) {
|
|
182
|
+
if (typeof log.log_id !== "string") {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
var trimmedLogID = log.log_id.trim();
|
|
186
|
+
if (trimmedLogID.length > 0 && !isUUID(trimmedLogID)) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
178
190
|
if (typeof log.value !== "number" || !Number.isFinite(log.value)) {
|
|
179
191
|
return false;
|
|
180
192
|
}
|
|
181
|
-
if (log.
|
|
193
|
+
if (log.agent !== undefined && typeof log.agent !== "string") {
|
|
182
194
|
return false;
|
|
183
195
|
}
|
|
184
196
|
if (log.meta !== undefined) {
|
|
@@ -207,13 +219,14 @@ var Client = /** @class */ (function () {
|
|
|
207
219
|
};
|
|
208
220
|
Client.prototype.normalizeLog = function (log) {
|
|
209
221
|
var normalizedLog = {
|
|
222
|
+
log_id: resolveLogID(log.log_id),
|
|
210
223
|
event: log.event.trim(),
|
|
211
224
|
value: log.value,
|
|
212
225
|
};
|
|
213
|
-
if (typeof log.
|
|
214
|
-
var
|
|
215
|
-
if (
|
|
216
|
-
normalizedLog.
|
|
226
|
+
if (typeof log.agent === "string") {
|
|
227
|
+
var trimmedAgent = log.agent.trim();
|
|
228
|
+
if (trimmedAgent.length > 0) {
|
|
229
|
+
normalizedLog.agent = trimmedAgent;
|
|
217
230
|
}
|
|
218
231
|
}
|
|
219
232
|
if (log.meta !== undefined) {
|
|
@@ -227,31 +240,23 @@ var Client = /** @class */ (function () {
|
|
|
227
240
|
};
|
|
228
241
|
Client.prototype.log = function (log) {
|
|
229
242
|
return __awaiter(this, void 0, void 0, function () {
|
|
230
|
-
var normalizedLog
|
|
243
|
+
var normalizedLog;
|
|
231
244
|
var _this = this;
|
|
232
245
|
return __generator(this, function (_a) {
|
|
233
246
|
switch (_a.label) {
|
|
234
247
|
case 0:
|
|
235
248
|
if (!this.isValidLog(log)) {
|
|
236
|
-
throw new ChirpierError("Invalid log format: event must not be empty, value must be a finite number,
|
|
249
|
+
throw new ChirpierError("Invalid log format: log_id must be a UUID when provided, event must not be empty, value must be a finite number, agent must be a string when provided, meta must be JSON-encodable, and occurred_at must be within the last 30 days and no more than 1 day in the future", "INVALID_LOG");
|
|
237
250
|
}
|
|
238
251
|
normalizedLog = this.normalizeLog(log);
|
|
239
|
-
queueFull = false;
|
|
240
252
|
return [4 /*yield*/, this.queueLock.acquire("queue", function () { return __awaiter(_this, void 0, void 0, function () {
|
|
241
253
|
return __generator(this, function (_a) {
|
|
242
|
-
|
|
243
|
-
queueFull = true;
|
|
244
|
-
return [2 /*return*/];
|
|
245
|
-
}
|
|
246
|
-
this.logQueue.push({ log: normalizedLog, timestamp: Date.now(), retryCount: 0 });
|
|
254
|
+
this.logQueue.push({ log: normalizedLog, timestamp: Date.now() });
|
|
247
255
|
return [2 /*return*/];
|
|
248
256
|
});
|
|
249
257
|
}); })];
|
|
250
258
|
case 1:
|
|
251
259
|
_a.sent();
|
|
252
|
-
if (queueFull) {
|
|
253
|
-
throw new ChirpierError("Log queue is full (max size: ".concat(this.maxQueueSize, ")"), "QUEUE_FULL");
|
|
254
|
-
}
|
|
255
260
|
if (!(this.logQueue.length >= this.batchSize)) return [3 /*break*/, 3];
|
|
256
261
|
return [4 /*yield*/, this.flushQueue()];
|
|
257
262
|
case 2:
|
|
@@ -273,7 +278,7 @@ var Client = /** @class */ (function () {
|
|
|
273
278
|
return __generator(this, function (_a) {
|
|
274
279
|
switch (_a.label) {
|
|
275
280
|
case 0: return [4 /*yield*/, this.flushLock.acquire("flush", function () { return __awaiter(_this, void 0, void 0, function () {
|
|
276
|
-
var logsToSend, error_1
|
|
281
|
+
var logsToSend, error_1;
|
|
277
282
|
var _this = this;
|
|
278
283
|
return __generator(this, function (_a) {
|
|
279
284
|
switch (_a.label) {
|
|
@@ -312,21 +317,9 @@ var Client = /** @class */ (function () {
|
|
|
312
317
|
if (this.logLevel >= 1 /* LogLevel.Error */) {
|
|
313
318
|
console.error("Failed to send logs:", error_1);
|
|
314
319
|
}
|
|
315
|
-
retryableLogs_1 = [];
|
|
316
|
-
for (_i = 0, logsToSend_1 = logsToSend; _i < logsToSend_1.length; _i++) {
|
|
317
|
-
queuedLog = logsToSend_1[_i];
|
|
318
|
-
if (queuedLog.retryCount >= this.retries) {
|
|
319
|
-
if (this.logLevel >= 1 /* LogLevel.Error */) {
|
|
320
|
-
console.error("Dropping log after ".concat(this.retries, " retries:"), queuedLog.log);
|
|
321
|
-
}
|
|
322
|
-
continue;
|
|
323
|
-
}
|
|
324
|
-
queuedLog.retryCount++;
|
|
325
|
-
retryableLogs_1.push(queuedLog);
|
|
326
|
-
}
|
|
327
320
|
return [4 /*yield*/, this.queueLock.acquire("logQueue", function () { return __awaiter(_this, void 0, void 0, function () {
|
|
328
321
|
return __generator(this, function (_a) {
|
|
329
|
-
this.logQueue = __spreadArray(__spreadArray([],
|
|
322
|
+
this.logQueue = __spreadArray(__spreadArray([], logsToSend, true), this.logQueue, true);
|
|
330
323
|
return [2 /*return*/];
|
|
331
324
|
});
|
|
332
325
|
}); })];
|
|
@@ -479,10 +472,10 @@ var Client = /** @class */ (function () {
|
|
|
479
472
|
});
|
|
480
473
|
});
|
|
481
474
|
};
|
|
482
|
-
Client.prototype.getAlertDeliveries = function (
|
|
483
|
-
|
|
484
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
475
|
+
Client.prototype.getAlertDeliveries = function (alertID_1) {
|
|
476
|
+
return __awaiter(this, arguments, void 0, function (alertID, options) {
|
|
485
477
|
var params, suffix, response;
|
|
478
|
+
if (options === void 0) { options = {}; }
|
|
486
479
|
return __generator(this, function (_a) {
|
|
487
480
|
switch (_a.label) {
|
|
488
481
|
case 0:
|
|
@@ -543,10 +536,10 @@ var Client = /** @class */ (function () {
|
|
|
543
536
|
});
|
|
544
537
|
});
|
|
545
538
|
};
|
|
546
|
-
Client.prototype.getEventLogs = function (
|
|
547
|
-
|
|
548
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
539
|
+
Client.prototype.getEventLogs = function (eventID_1) {
|
|
540
|
+
return __awaiter(this, arguments, void 0, function (eventID, options) {
|
|
549
541
|
var params, suffix, response;
|
|
542
|
+
if (options === void 0) { options = {}; }
|
|
550
543
|
return __generator(this, function (_a) {
|
|
551
544
|
switch (_a.label) {
|
|
552
545
|
case 0:
|
|
@@ -590,13 +583,24 @@ function createClient(config) {
|
|
|
590
583
|
if (config === void 0) { config = {}; }
|
|
591
584
|
return new Client(config);
|
|
592
585
|
}
|
|
593
|
-
exports.createClient = createClient;
|
|
594
586
|
function isNodeEnvironment() {
|
|
595
587
|
return typeof process !== "undefined" && !!(process.versions && process.versions.node);
|
|
596
588
|
}
|
|
597
589
|
function isValidAPIKey(token) {
|
|
598
590
|
return token.startsWith("chp_") && token.length > "chp_".length;
|
|
599
591
|
}
|
|
592
|
+
function isUUID(value) {
|
|
593
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
594
|
+
}
|
|
595
|
+
function resolveLogID(logID) {
|
|
596
|
+
if (typeof logID === "string") {
|
|
597
|
+
var trimmedLogID = logID.trim();
|
|
598
|
+
if (trimmedLogID.length > 0) {
|
|
599
|
+
return trimmedLogID;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return (0, uuid_1.v7)();
|
|
603
|
+
}
|
|
600
604
|
function loadDotEnvKey() {
|
|
601
605
|
if (!isNodeEnvironment()) {
|
|
602
606
|
return undefined;
|
|
@@ -655,7 +659,6 @@ function initialize(options) {
|
|
|
655
659
|
throw error;
|
|
656
660
|
}
|
|
657
661
|
}
|
|
658
|
-
exports.initialize = initialize;
|
|
659
662
|
function logEvent(log) {
|
|
660
663
|
return __awaiter(this, void 0, void 0, function () {
|
|
661
664
|
return __generator(this, function (_a) {
|
|
@@ -672,7 +675,6 @@ function logEvent(log) {
|
|
|
672
675
|
});
|
|
673
676
|
});
|
|
674
677
|
}
|
|
675
|
-
exports.logEvent = logEvent;
|
|
676
678
|
function stop() {
|
|
677
679
|
return __awaiter(this, void 0, void 0, function () {
|
|
678
680
|
return __generator(this, function (_a) {
|
|
@@ -690,7 +692,6 @@ function stop() {
|
|
|
690
692
|
});
|
|
691
693
|
});
|
|
692
694
|
}
|
|
693
|
-
exports.stop = stop;
|
|
694
695
|
function flush() {
|
|
695
696
|
return __awaiter(this, void 0, void 0, function () {
|
|
696
697
|
return __generator(this, function (_a) {
|
|
@@ -707,4 +708,3 @@ function flush() {
|
|
|
707
708
|
});
|
|
708
709
|
});
|
|
709
710
|
}
|
|
710
|
-
exports.flush = flush;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chirpier/chirpier-js",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Chirpier SDK for JavaScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chirpier",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"axios-retry": "^4.5.0",
|
|
33
33
|
"dotenv": "^16.4.7",
|
|
34
34
|
"ts-node": "^10.9.2",
|
|
35
|
-
"tslib": "^2.3.0"
|
|
35
|
+
"tslib": "^2.3.0",
|
|
36
|
+
"uuid": "^11.1.0"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
38
39
|
"@eslint/js": "^9.15.0",
|
|
@@ -41,13 +42,12 @@
|
|
|
41
42
|
"@types/jest": "^29.5.13",
|
|
42
43
|
"@types/mocha": "^10.0.8",
|
|
43
44
|
"@types/node": "^22.7.5",
|
|
44
|
-
"@types/uuid": "^10.0.0",
|
|
45
45
|
"axios-mock-adapter": "^2.0.0",
|
|
46
46
|
"eslint": "^9.15.0",
|
|
47
47
|
"globals": "^15.12.0",
|
|
48
48
|
"jest": "^29.7.0",
|
|
49
49
|
"ts-jest": "^29.2.4",
|
|
50
|
-
"typescript": "^
|
|
50
|
+
"typescript": "^5.6.3",
|
|
51
51
|
"typescript-eslint": "^8.15.0",
|
|
52
52
|
"webpack": "^5.52.0",
|
|
53
53
|
"webpack-cli": "^4.8.0"
|
|
@@ -134,7 +134,7 @@ describe("Chirpier SDK", () => {
|
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
const log: Log = {
|
|
137
|
-
|
|
137
|
+
agent: "api.worker",
|
|
138
138
|
event: "request.finished",
|
|
139
139
|
value: 1,
|
|
140
140
|
};
|
|
@@ -146,14 +146,38 @@ describe("Chirpier SDK", () => {
|
|
|
146
146
|
expect(mock.history.post[0].url).toBe(DEFAULT_API_ENDPOINT);
|
|
147
147
|
expect(JSON.parse(mock.history.post[0].data)).toEqual([
|
|
148
148
|
{
|
|
149
|
-
|
|
149
|
+
log_id: expect.any(String),
|
|
150
|
+
agent: "api.worker",
|
|
150
151
|
event: "request.finished",
|
|
151
152
|
value: 1,
|
|
152
153
|
},
|
|
153
154
|
]);
|
|
155
|
+
expect(JSON.parse(mock.history.post[0].data)[0].log_id).toMatch(
|
|
156
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
|
157
|
+
);
|
|
154
158
|
});
|
|
155
159
|
|
|
156
|
-
test("
|
|
160
|
+
test("should preserve provided log_id", async () => {
|
|
161
|
+
const mock = new MockAdapter(axios);
|
|
162
|
+
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
163
|
+
|
|
164
|
+
initialize({
|
|
165
|
+
key: "chp_log_id_key",
|
|
166
|
+
logLevel: LogLevel.None,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
await logEvent({
|
|
170
|
+
log_id: "9f97d65f-fb30-4062-b4d0-8617c03fe4f6",
|
|
171
|
+
event: "request.finished",
|
|
172
|
+
value: 1,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
176
|
+
const payload = JSON.parse(mock.history.post[0].data);
|
|
177
|
+
expect(payload[0].log_id).toBe("9f97d65f-fb30-4062-b4d0-8617c03fe4f6");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("agent whitespace should be omitted", async () => {
|
|
157
181
|
const mock = new MockAdapter(axios);
|
|
158
182
|
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
159
183
|
|
|
@@ -163,14 +187,14 @@ describe("Chirpier SDK", () => {
|
|
|
163
187
|
});
|
|
164
188
|
|
|
165
189
|
await logEvent({
|
|
166
|
-
|
|
190
|
+
agent: " ",
|
|
167
191
|
event: "metric.tick",
|
|
168
192
|
value: 42,
|
|
169
193
|
});
|
|
170
194
|
|
|
171
195
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
172
196
|
const payload = JSON.parse(mock.history.post[0].data);
|
|
173
|
-
expect(payload[0].
|
|
197
|
+
expect(payload[0].agent).toBeUndefined();
|
|
174
198
|
});
|
|
175
199
|
|
|
176
200
|
test("should support meta payload", async () => {
|
|
@@ -183,7 +207,7 @@ describe("Chirpier SDK", () => {
|
|
|
183
207
|
});
|
|
184
208
|
|
|
185
209
|
await logEvent({
|
|
186
|
-
|
|
210
|
+
agent: "api.worker",
|
|
187
211
|
event: "request.finished",
|
|
188
212
|
value: 200,
|
|
189
213
|
meta: {
|
|
@@ -251,6 +275,14 @@ describe("Chirpier SDK", () => {
|
|
|
251
275
|
})
|
|
252
276
|
).rejects.toThrow(ChirpierError);
|
|
253
277
|
|
|
278
|
+
await expect(
|
|
279
|
+
logEvent({
|
|
280
|
+
log_id: "not-a-uuid",
|
|
281
|
+
event: "bad-log-id",
|
|
282
|
+
value: 1,
|
|
283
|
+
})
|
|
284
|
+
).rejects.toThrow(ChirpierError);
|
|
285
|
+
|
|
254
286
|
expect(mock.history.post.length).toBe(0);
|
|
255
287
|
});
|
|
256
288
|
|
package/src/index.ts
CHANGED
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
DEFAULT_TIMEOUT,
|
|
7
7
|
DEFAULT_BATCH_SIZE,
|
|
8
8
|
DEFAULT_FLUSH_DELAY,
|
|
9
|
-
MAX_QUEUE_SIZE,
|
|
10
9
|
DEFAULT_API_ENDPOINT,
|
|
11
10
|
DEFAULT_SERVICER_ENDPOINT,
|
|
12
11
|
} from "./constants";
|
|
13
12
|
import AsyncLock from "async-lock";
|
|
13
|
+
import { v7 as uuidv7 } from "uuid";
|
|
14
14
|
|
|
15
15
|
// Define logging levels as const enum for better tree-shaking
|
|
16
16
|
export const enum LogLevel {
|
|
@@ -29,6 +29,7 @@ export interface Config {
|
|
|
29
29
|
timeout?: number;
|
|
30
30
|
batchSize?: number;
|
|
31
31
|
flushDelay?: number;
|
|
32
|
+
/** @deprecated Queues are unbounded in memory. */
|
|
32
33
|
maxQueueSize?: number;
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -38,26 +39,23 @@ export interface Config {
|
|
|
38
39
|
export type Options = Config;
|
|
39
40
|
|
|
40
41
|
export interface Log {
|
|
41
|
-
|
|
42
|
+
log_id?: string;
|
|
43
|
+
agent?: string;
|
|
42
44
|
event: string;
|
|
43
45
|
value: number;
|
|
44
46
|
meta?: unknown;
|
|
45
47
|
occurred_at?: string | Date;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
export interface
|
|
50
|
+
export interface Event {
|
|
49
51
|
readonly event_id: string;
|
|
50
|
-
readonly
|
|
52
|
+
readonly agent?: string;
|
|
51
53
|
readonly event: string;
|
|
52
54
|
readonly title?: string;
|
|
53
55
|
readonly public: boolean;
|
|
54
56
|
readonly description?: string;
|
|
55
57
|
readonly unit?: string;
|
|
56
|
-
readonly
|
|
57
|
-
readonly default_aggregate: string;
|
|
58
|
-
readonly enabled: boolean;
|
|
59
|
-
readonly origin: string;
|
|
60
|
-
readonly archived_at?: string;
|
|
58
|
+
readonly timezone: string;
|
|
61
59
|
readonly created_at?: string;
|
|
62
60
|
}
|
|
63
61
|
|
|
@@ -79,7 +77,7 @@ export interface Alert {
|
|
|
79
77
|
readonly alert_id: string;
|
|
80
78
|
readonly policy_id: string;
|
|
81
79
|
readonly event_id: string;
|
|
82
|
-
readonly
|
|
80
|
+
readonly agent?: string;
|
|
83
81
|
readonly event: string;
|
|
84
82
|
readonly title: string;
|
|
85
83
|
readonly period: string;
|
|
@@ -111,7 +109,7 @@ export interface AlertDelivery {
|
|
|
111
109
|
|
|
112
110
|
export interface EventLogPoint {
|
|
113
111
|
readonly event_id: string;
|
|
114
|
-
readonly
|
|
112
|
+
readonly agent?: string;
|
|
115
113
|
readonly event: string;
|
|
116
114
|
readonly period: string;
|
|
117
115
|
readonly occurred_at: string;
|
|
@@ -145,7 +143,6 @@ export class ChirpierError extends Error {
|
|
|
145
143
|
interface QueuedLog {
|
|
146
144
|
readonly log: Log;
|
|
147
145
|
readonly timestamp: number;
|
|
148
|
-
retryCount: number;
|
|
149
146
|
}
|
|
150
147
|
|
|
151
148
|
export class Client {
|
|
@@ -158,7 +155,6 @@ export class Client {
|
|
|
158
155
|
private logQueue: QueuedLog[] = [];
|
|
159
156
|
private readonly batchSize: number;
|
|
160
157
|
private readonly flushDelay: number;
|
|
161
|
-
private readonly maxQueueSize: number;
|
|
162
158
|
private flushTimeoutId: NodeJS.Timeout | null = null;
|
|
163
159
|
private readonly queueLock: AsyncLock;
|
|
164
160
|
private readonly flushLock: AsyncLock;
|
|
@@ -174,7 +170,7 @@ export class Client {
|
|
|
174
170
|
timeout = DEFAULT_TIMEOUT,
|
|
175
171
|
batchSize = DEFAULT_BATCH_SIZE,
|
|
176
172
|
flushDelay = DEFAULT_FLUSH_DELAY,
|
|
177
|
-
maxQueueSize
|
|
173
|
+
maxQueueSize,
|
|
178
174
|
} = options;
|
|
179
175
|
|
|
180
176
|
const key = resolveAPIKey(providedKey);
|
|
@@ -226,10 +222,6 @@ export class Client {
|
|
|
226
222
|
if (flushDelay < 0) {
|
|
227
223
|
throw new ChirpierError("Flush delay must be non-negative", "INVALID_FLUSH_DELAY");
|
|
228
224
|
}
|
|
229
|
-
if (maxQueueSize <= 0 || !Number.isInteger(maxQueueSize)) {
|
|
230
|
-
throw new ChirpierError("Max queue size must be a positive integer", "INVALID_QUEUE_SIZE");
|
|
231
|
-
}
|
|
232
|
-
|
|
233
225
|
this.apiEndpoint = apiEndpoint ?? DEFAULT_API_ENDPOINT;
|
|
234
226
|
this.servicerEndpoint = servicerEndpoint ?? DEFAULT_SERVICER_ENDPOINT;
|
|
235
227
|
this.apiKey = key;
|
|
@@ -237,11 +229,11 @@ export class Client {
|
|
|
237
229
|
this.timeout = timeout;
|
|
238
230
|
this.batchSize = batchSize;
|
|
239
231
|
this.flushDelay = flushDelay;
|
|
240
|
-
this.maxQueueSize = maxQueueSize;
|
|
241
232
|
this.logLevel = logLevel;
|
|
242
233
|
|
|
243
|
-
|
|
244
|
-
this.
|
|
234
|
+
void maxQueueSize;
|
|
235
|
+
this.queueLock = new AsyncLock();
|
|
236
|
+
this.flushLock = new AsyncLock();
|
|
245
237
|
|
|
246
238
|
this.axiosInstance = axios.create({
|
|
247
239
|
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
@@ -280,11 +272,22 @@ export class Client {
|
|
|
280
272
|
return false;
|
|
281
273
|
}
|
|
282
274
|
|
|
275
|
+
if (log.log_id !== undefined) {
|
|
276
|
+
if (typeof log.log_id !== "string") {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const trimmedLogID = log.log_id.trim();
|
|
281
|
+
if (trimmedLogID.length > 0 && !isUUID(trimmedLogID)) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
283
286
|
if (typeof log.value !== "number" || !Number.isFinite(log.value)) {
|
|
284
287
|
return false;
|
|
285
288
|
}
|
|
286
289
|
|
|
287
|
-
if (log.
|
|
290
|
+
if (log.agent !== undefined && typeof log.agent !== "string") {
|
|
288
291
|
return false;
|
|
289
292
|
}
|
|
290
293
|
|
|
@@ -319,14 +322,15 @@ export class Client {
|
|
|
319
322
|
|
|
320
323
|
private normalizeLog(log: Log): Log {
|
|
321
324
|
const normalizedLog: Log = {
|
|
325
|
+
log_id: resolveLogID(log.log_id),
|
|
322
326
|
event: log.event.trim(),
|
|
323
327
|
value: log.value,
|
|
324
328
|
};
|
|
325
329
|
|
|
326
|
-
if (typeof log.
|
|
327
|
-
const
|
|
328
|
-
if (
|
|
329
|
-
normalizedLog.
|
|
330
|
+
if (typeof log.agent === "string") {
|
|
331
|
+
const trimmedAgent = log.agent.trim();
|
|
332
|
+
if (trimmedAgent.length > 0) {
|
|
333
|
+
normalizedLog.agent = trimmedAgent;
|
|
330
334
|
}
|
|
331
335
|
}
|
|
332
336
|
|
|
@@ -346,31 +350,17 @@ export class Client {
|
|
|
346
350
|
public async log(log: Log): Promise<void> {
|
|
347
351
|
if (!this.isValidLog(log)) {
|
|
348
352
|
throw new ChirpierError(
|
|
349
|
-
"Invalid log format: event must not be empty, value must be a finite number,
|
|
353
|
+
"Invalid log format: log_id must be a UUID when provided, event must not be empty, value must be a finite number, agent must be a string when provided, meta must be JSON-encodable, and occurred_at must be within the last 30 days and no more than 1 day in the future",
|
|
350
354
|
"INVALID_LOG"
|
|
351
355
|
);
|
|
352
356
|
}
|
|
353
357
|
|
|
354
358
|
const normalizedLog = this.normalizeLog(log);
|
|
355
359
|
|
|
356
|
-
let queueFull = false;
|
|
357
|
-
|
|
358
360
|
await this.queueLock.acquire("queue", async () => {
|
|
359
|
-
|
|
360
|
-
queueFull = true;
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
this.logQueue.push({ log: normalizedLog, timestamp: Date.now(), retryCount: 0 });
|
|
361
|
+
this.logQueue.push({ log: normalizedLog, timestamp: Date.now() });
|
|
365
362
|
});
|
|
366
363
|
|
|
367
|
-
if (queueFull) {
|
|
368
|
-
throw new ChirpierError(
|
|
369
|
-
`Log queue is full (max size: ${this.maxQueueSize})`,
|
|
370
|
-
"QUEUE_FULL"
|
|
371
|
-
);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
364
|
if (this.logQueue.length >= this.batchSize) {
|
|
375
365
|
await this.flushQueue();
|
|
376
366
|
} else if (!this.flushTimeoutId) {
|
|
@@ -412,24 +402,8 @@ export class Client {
|
|
|
412
402
|
console.error("Failed to send logs:", error);
|
|
413
403
|
}
|
|
414
404
|
|
|
415
|
-
const retryableLogs: QueuedLog[] = [];
|
|
416
|
-
for (const queuedLog of logsToSend) {
|
|
417
|
-
if (queuedLog.retryCount >= this.retries) {
|
|
418
|
-
if (this.logLevel >= LogLevel.Error) {
|
|
419
|
-
console.error(
|
|
420
|
-
`Dropping log after ${this.retries} retries:`,
|
|
421
|
-
queuedLog.log
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
|
-
continue;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
queuedLog.retryCount++;
|
|
428
|
-
retryableLogs.push(queuedLog);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
405
|
await this.queueLock.acquire("logQueue", async () => {
|
|
432
|
-
this.logQueue = [...
|
|
406
|
+
this.logQueue = [...logsToSend, ...this.logQueue];
|
|
433
407
|
});
|
|
434
408
|
}
|
|
435
409
|
});
|
|
@@ -456,21 +430,21 @@ export class Client {
|
|
|
456
430
|
await this.shutdown();
|
|
457
431
|
}
|
|
458
432
|
|
|
459
|
-
public async listEvents(): Promise<
|
|
460
|
-
const response = await this.axiosInstance.get<
|
|
433
|
+
public async listEvents(): Promise<Event[]> {
|
|
434
|
+
const response = await this.axiosInstance.get<Event[]>(`${this.servicerEndpoint}/events`);
|
|
461
435
|
return response.data;
|
|
462
436
|
}
|
|
463
437
|
|
|
464
|
-
public async getEvent(eventID: string): Promise<
|
|
465
|
-
const response = await this.axiosInstance.get<
|
|
438
|
+
public async getEvent(eventID: string): Promise<Event> {
|
|
439
|
+
const response = await this.axiosInstance.get<Event>(`${this.servicerEndpoint}/events/${eventID}`);
|
|
466
440
|
return response.data;
|
|
467
441
|
}
|
|
468
442
|
|
|
469
443
|
public async updateEvent(
|
|
470
444
|
eventID: string,
|
|
471
|
-
payload: Partial<Omit<
|
|
472
|
-
): Promise<
|
|
473
|
-
const response = await this.axiosInstance.put<
|
|
445
|
+
payload: Partial<Omit<Event, "event_id" | "created_at">>
|
|
446
|
+
): Promise<Event> {
|
|
447
|
+
const response = await this.axiosInstance.put<Event>(
|
|
474
448
|
`${this.servicerEndpoint}/events/${eventID}`,
|
|
475
449
|
payload
|
|
476
450
|
);
|
|
@@ -562,6 +536,21 @@ function isValidAPIKey(token: string): boolean {
|
|
|
562
536
|
return token.startsWith("chp_") && token.length > "chp_".length;
|
|
563
537
|
}
|
|
564
538
|
|
|
539
|
+
function isUUID(value: string): boolean {
|
|
540
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function resolveLogID(logID?: string): string {
|
|
544
|
+
if (typeof logID === "string") {
|
|
545
|
+
const trimmedLogID = logID.trim();
|
|
546
|
+
if (trimmedLogID.length > 0) {
|
|
547
|
+
return trimmedLogID;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return uuidv7();
|
|
552
|
+
}
|
|
553
|
+
|
|
565
554
|
function loadDotEnvKey(): string | undefined {
|
|
566
555
|
if (!isNodeEnvironment()) {
|
|
567
556
|
return undefined;
|