@mihari/logger-core 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mihari Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # @mihari/logger-core
2
+
3
+ Core transport, batching, and client logic for the mihari log collection library.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @mihari/logger-core
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { MihariClient } from "@mihari/logger-core";
15
+
16
+ const client = new MihariClient({
17
+ token: "your-api-token",
18
+ endpoint: "https://logs.example.com",
19
+ batchSize: 10,
20
+ flushInterval: 5000,
21
+ });
22
+
23
+ client.info("Hello from core", { module: "auth" });
24
+
25
+ // Flush manually
26
+ await client.flush();
27
+
28
+ // Shutdown gracefully
29
+ await client.shutdown();
30
+ ```
31
+
32
+ ## Components
33
+
34
+ - **MihariClient** - Base client with info/warn/error/debug/fatal methods
35
+ - **HttpTransport** - HTTP POST with Bearer auth, gzip support, and retry with exponential backoff
36
+ - **Batcher** - Configurable batching with flush intervals and queue size limits
37
+
38
+ ## License
39
+
40
+ MIT
@@ -0,0 +1,32 @@
1
+ import { LogEntry, BatchOptions } from "@mihari/logger-types";
2
+ export type FlushCallback = (logs: readonly LogEntry[]) => Promise<void>;
3
+ export declare class Batcher {
4
+ private queue;
5
+ private readonly batchSize;
6
+ private readonly flushIntervalMs;
7
+ private readonly maxQueueSize;
8
+ private readonly onFlush;
9
+ private timer;
10
+ constructor(options: BatchOptions, onFlush: FlushCallback);
11
+ /**
12
+ * Adds a log entry to the queue. Triggers a flush if the
13
+ * batch size threshold is reached.
14
+ */
15
+ add(entry: LogEntry): void;
16
+ /**
17
+ * Flushes all queued log entries by calling the onFlush callback.
18
+ * Returns a promise that resolves when the flush is complete.
19
+ */
20
+ flush(): Promise<void>;
21
+ /**
22
+ * Returns the number of entries currently in the queue.
23
+ */
24
+ get size(): number;
25
+ /**
26
+ * Stops the periodic flush timer and flushes remaining entries.
27
+ */
28
+ shutdown(): Promise<void>;
29
+ private startTimer;
30
+ private stopTimer;
31
+ }
32
+ //# sourceMappingURL=batcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batcher.d.ts","sourceRoot":"","sources":["../src/batcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAM9D,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,SAAS,QAAQ,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzE,qBAAa,OAAO;IAClB,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,KAAK,CAA+C;gBAEhD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa;IAQzD;;;OAGG;IACH,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAa1B;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB5B;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAK/B,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,SAAS;CAMlB"}
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Batcher = void 0;
4
+ const DEFAULT_BATCH_SIZE = 10;
5
+ const DEFAULT_FLUSH_INTERVAL_MS = 5000;
6
+ const DEFAULT_MAX_QUEUE_SIZE = 1000;
7
+ class Batcher {
8
+ constructor(options, onFlush) {
9
+ this.queue = [];
10
+ this.timer = null;
11
+ this.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
12
+ this.flushIntervalMs = options.flushInterval ?? DEFAULT_FLUSH_INTERVAL_MS;
13
+ this.maxQueueSize = options.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE;
14
+ this.onFlush = onFlush;
15
+ this.startTimer();
16
+ }
17
+ /**
18
+ * Adds a log entry to the queue. Triggers a flush if the
19
+ * batch size threshold is reached.
20
+ */
21
+ add(entry) {
22
+ if (this.queue.length >= this.maxQueueSize) {
23
+ // Drop the oldest entry when the queue is full
24
+ this.queue.shift();
25
+ }
26
+ this.queue = [...this.queue, entry];
27
+ if (this.queue.length >= this.batchSize) {
28
+ void this.flush();
29
+ }
30
+ }
31
+ /**
32
+ * Flushes all queued log entries by calling the onFlush callback.
33
+ * Returns a promise that resolves when the flush is complete.
34
+ */
35
+ async flush() {
36
+ if (this.queue.length === 0) {
37
+ return;
38
+ }
39
+ const batch = [...this.queue];
40
+ this.queue = [];
41
+ try {
42
+ await this.onFlush(batch);
43
+ }
44
+ catch (err) {
45
+ // Re-add entries to the front of the queue on failure,
46
+ // respecting the max queue size
47
+ const combined = [...batch, ...this.queue];
48
+ this.queue = combined.slice(0, this.maxQueueSize);
49
+ // Rethrow so callers know the flush failed
50
+ throw err;
51
+ }
52
+ }
53
+ /**
54
+ * Returns the number of entries currently in the queue.
55
+ */
56
+ get size() {
57
+ return this.queue.length;
58
+ }
59
+ /**
60
+ * Stops the periodic flush timer and flushes remaining entries.
61
+ */
62
+ async shutdown() {
63
+ this.stopTimer();
64
+ await this.flush();
65
+ }
66
+ startTimer() {
67
+ if (this.flushIntervalMs > 0) {
68
+ this.timer = setInterval(() => {
69
+ void this.flush();
70
+ }, this.flushIntervalMs);
71
+ // Allow the Node.js process to exit even if the timer is active
72
+ if (typeof this.timer === "object" && "unref" in this.timer) {
73
+ this.timer.unref();
74
+ }
75
+ }
76
+ }
77
+ stopTimer() {
78
+ if (this.timer !== null) {
79
+ clearInterval(this.timer);
80
+ this.timer = null;
81
+ }
82
+ }
83
+ }
84
+ exports.Batcher = Batcher;
85
+ //# sourceMappingURL=batcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batcher.js","sourceRoot":"","sources":["../src/batcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { LogLevel, MihariConfig, CompressFn } from "@mihari/logger-types";
2
+ import { HttpTransport } from "./transport";
3
+ import { Batcher } from "./batcher";
4
+ export declare class MihariClient {
5
+ protected readonly config: MihariConfig;
6
+ protected readonly transport: HttpTransport;
7
+ protected readonly batcher: Batcher;
8
+ constructor(config: MihariConfig);
9
+ /**
10
+ * Sets the compression function used by the transport layer.
11
+ */
12
+ setCompressFn(fn: CompressFn): void;
13
+ /**
14
+ * Logs a debug-level message.
15
+ */
16
+ debug(message: string, metadata?: Record<string, unknown>): void;
17
+ /**
18
+ * Logs an info-level message.
19
+ */
20
+ info(message: string, metadata?: Record<string, unknown>): void;
21
+ /**
22
+ * Logs a warn-level message.
23
+ */
24
+ warn(message: string, metadata?: Record<string, unknown>): void;
25
+ /**
26
+ * Logs an error-level message.
27
+ */
28
+ error(message: string, metadata?: Record<string, unknown>): void;
29
+ /**
30
+ * Logs a fatal-level message.
31
+ */
32
+ fatal(message: string, metadata?: Record<string, unknown>): void;
33
+ /**
34
+ * Flushes all pending log entries.
35
+ */
36
+ flush(): Promise<void>;
37
+ /**
38
+ * Shuts down the client, flushing remaining entries.
39
+ */
40
+ shutdown(): Promise<void>;
41
+ /**
42
+ * Creates a log entry and adds it to the batch queue.
43
+ * Subclasses can override getDefaultMetadata() to inject
44
+ * environment-specific fields.
45
+ */
46
+ log(level: LogLevel, message: string, metadata?: Record<string, unknown>): void;
47
+ /**
48
+ * Returns default metadata to include with every log entry.
49
+ * Override in subclasses to add environment-specific fields.
50
+ */
51
+ protected getDefaultMetadata(): Record<string, unknown>;
52
+ }
53
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAY,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,qBAAa,YAAY;IACvB,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IACxC,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAC5C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;gBAExB,MAAM,EAAE,YAAY;IAsBhC;;OAEG;IACH,aAAa,CAAC,EAAE,EAAE,UAAU,GAAG,IAAI;IAInC;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIhE;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI/D;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI/D;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIhE;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIhE;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B;;;;OAIG;IACI,GAAG,CACR,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,IAAI;IAaP;;;OAGG;IACH,SAAS,CAAC,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAGxD"}
package/dist/client.js ADDED
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MihariClient = void 0;
4
+ const logger_types_1 = require("@mihari/logger-types");
5
+ const transport_1 = require("./transport");
6
+ const batcher_1 = require("./batcher");
7
+ const utils_1 = require("./utils");
8
+ class MihariClient {
9
+ constructor(config) {
10
+ this.config = config;
11
+ this.transport = new transport_1.HttpTransport({
12
+ token: config.token,
13
+ endpoint: config.endpoint,
14
+ compression: config.compression,
15
+ retries: config.retries,
16
+ });
17
+ this.batcher = new batcher_1.Batcher({
18
+ batchSize: config.batchSize,
19
+ flushInterval: config.flushInterval,
20
+ maxQueueSize: config.maxQueueSize,
21
+ }, async (logs) => {
22
+ await this.transport.send(logs);
23
+ });
24
+ }
25
+ /**
26
+ * Sets the compression function used by the transport layer.
27
+ */
28
+ setCompressFn(fn) {
29
+ this.transport.setCompressFn(fn);
30
+ }
31
+ /**
32
+ * Logs a debug-level message.
33
+ */
34
+ debug(message, metadata) {
35
+ this.log(logger_types_1.LogLevel.Debug, message, metadata);
36
+ }
37
+ /**
38
+ * Logs an info-level message.
39
+ */
40
+ info(message, metadata) {
41
+ this.log(logger_types_1.LogLevel.Info, message, metadata);
42
+ }
43
+ /**
44
+ * Logs a warn-level message.
45
+ */
46
+ warn(message, metadata) {
47
+ this.log(logger_types_1.LogLevel.Warn, message, metadata);
48
+ }
49
+ /**
50
+ * Logs an error-level message.
51
+ */
52
+ error(message, metadata) {
53
+ this.log(logger_types_1.LogLevel.Error, message, metadata);
54
+ }
55
+ /**
56
+ * Logs a fatal-level message.
57
+ */
58
+ fatal(message, metadata) {
59
+ this.log(logger_types_1.LogLevel.Fatal, message, metadata);
60
+ }
61
+ /**
62
+ * Flushes all pending log entries.
63
+ */
64
+ async flush() {
65
+ await this.batcher.flush();
66
+ }
67
+ /**
68
+ * Shuts down the client, flushing remaining entries.
69
+ */
70
+ async shutdown() {
71
+ await this.batcher.shutdown();
72
+ }
73
+ /**
74
+ * Creates a log entry and adds it to the batch queue.
75
+ * Subclasses can override getDefaultMetadata() to inject
76
+ * environment-specific fields.
77
+ */
78
+ log(level, message, metadata) {
79
+ const entry = {
80
+ dt: (0, utils_1.isoTimestamp)(),
81
+ level,
82
+ message,
83
+ ...this.getDefaultMetadata(),
84
+ ...(this.config.metadata ?? {}),
85
+ ...(metadata ?? {}),
86
+ };
87
+ this.batcher.add(entry);
88
+ }
89
+ /**
90
+ * Returns default metadata to include with every log entry.
91
+ * Override in subclasses to add environment-specific fields.
92
+ */
93
+ getDefaultMetadata() {
94
+ return {};
95
+ }
96
+ }
97
+ exports.MihariClient = MihariClient;
98
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";;;AAAA,uDAAoF;AACpF,2CAA4C;AAC5C,uCAAoC;AACpC,mCAAuC;AAEvC,MAAa,YAAY;IAKvB,YAAY,MAAoB;QAC9B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,IAAI,CAAC,SAAS,GAAG,IAAI,yBAAa,CAAC;YACjC,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,OAAO,EAAE,MAAM,CAAC,OAAO;SACxB,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,GAAG,IAAI,iBAAO,CACxB;YACE,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,aAAa,EAAE,MAAM,CAAC,aAAa;YACnC,YAAY,EAAE,MAAM,CAAC,YAAY;SAClC,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;YACb,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC,CACF,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,EAAc;QAC1B,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAe,EAAE,QAAkC;QACvD,IAAI,CAAC,GAAG,CAAC,uBAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,IAAI,CAAC,OAAe,EAAE,QAAkC;QACtD,IAAI,CAAC,GAAG,CAAC,uBAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,IAAI,CAAC,OAAe,EAAE,QAAkC;QACtD,IAAI,CAAC,GAAG,CAAC,uBAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAe,EAAE,QAAkC;QACvD,IAAI,CAAC,GAAG,CAAC,uBAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAe,EAAE,QAAkC;QACvD,IAAI,CAAC,GAAG,CAAC,uBAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;IAChC,CAAC;IAED;;;;OAIG;IACI,GAAG,CACR,KAAe,EACf,OAAe,EACf,QAAkC;QAElC,MAAM,KAAK,GAAa;YACtB,EAAE,EAAE,IAAA,oBAAY,GAAE;YAClB,KAAK;YACL,OAAO;YACP,GAAG,IAAI,CAAC,kBAAkB,EAAE;YAC5B,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;YAC/B,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC;SACpB,CAAC;QAEF,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED;;;OAGG;IACO,kBAAkB;QAC1B,OAAO,EAAE,CAAC;IACZ,CAAC;CACF;AAhHD,oCAgHC"}
@@ -0,0 +1,7 @@
1
+ export { MihariClient } from "./client";
2
+ export { HttpTransport } from "./transport";
3
+ export { Batcher } from "./batcher";
4
+ export type { FlushCallback } from "./batcher";
5
+ export { isoTimestamp, isBrowser, isNode, sleep } from "./utils";
6
+ export { LogLevel, LogEntry, MihariConfig, TransportOptions, BatchOptions, TransportResponse, TransportError, CompressFn, } from "@mihari/logger-types";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,UAAU,GACX,MAAM,sBAAsB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LogLevel = exports.sleep = exports.isNode = exports.isBrowser = exports.isoTimestamp = exports.Batcher = exports.HttpTransport = exports.MihariClient = void 0;
4
+ var client_1 = require("./client");
5
+ Object.defineProperty(exports, "MihariClient", { enumerable: true, get: function () { return client_1.MihariClient; } });
6
+ var transport_1 = require("./transport");
7
+ Object.defineProperty(exports, "HttpTransport", { enumerable: true, get: function () { return transport_1.HttpTransport; } });
8
+ var batcher_1 = require("./batcher");
9
+ Object.defineProperty(exports, "Batcher", { enumerable: true, get: function () { return batcher_1.Batcher; } });
10
+ var utils_1 = require("./utils");
11
+ Object.defineProperty(exports, "isoTimestamp", { enumerable: true, get: function () { return utils_1.isoTimestamp; } });
12
+ Object.defineProperty(exports, "isBrowser", { enumerable: true, get: function () { return utils_1.isBrowser; } });
13
+ Object.defineProperty(exports, "isNode", { enumerable: true, get: function () { return utils_1.isNode; } });
14
+ Object.defineProperty(exports, "sleep", { enumerable: true, get: function () { return utils_1.sleep; } });
15
+ var logger_types_1 = require("@mihari/logger-types");
16
+ Object.defineProperty(exports, "LogLevel", { enumerable: true, get: function () { return logger_types_1.LogLevel; } });
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,mCAAwC;AAA/B,sGAAA,YAAY,OAAA;AACrB,yCAA4C;AAAnC,0GAAA,aAAa,OAAA;AACtB,qCAAoC;AAA3B,kGAAA,OAAO,OAAA;AAEhB,iCAAiE;AAAxD,qGAAA,YAAY,OAAA;AAAE,kGAAA,SAAS,OAAA;AAAE,+FAAA,MAAM,OAAA;AAAE,8FAAA,KAAK,OAAA;AAC/C,qDAS8B;AAR5B,wGAAA,QAAQ,OAAA"}
@@ -0,0 +1,21 @@
1
+ import { LogEntry, TransportOptions, TransportResponse, CompressFn } from "@mihari/logger-types";
2
+ export declare class HttpTransport {
3
+ private readonly token;
4
+ private readonly endpoint;
5
+ private readonly compression;
6
+ private readonly maxRetries;
7
+ private compressFn;
8
+ constructor(options: TransportOptions);
9
+ /**
10
+ * Sets the compression function. This allows node and browser
11
+ * environments to inject their own gzip implementation.
12
+ */
13
+ setCompressFn(fn: CompressFn): void;
14
+ /**
15
+ * Sends an array of log entries to the ingestion endpoint with
16
+ * retry logic using exponential backoff.
17
+ */
18
+ send(logs: readonly LogEntry[]): Promise<TransportResponse>;
19
+ private doFetch;
20
+ }
21
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAMjG,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,UAAU,CAA2B;gBAEjC,OAAO,EAAE,gBAAgB;IAOrC;;;OAGG;IACH,aAAa,CAAC,EAAE,EAAE,UAAU,GAAG,IAAI;IAInC;;;OAGG;IACG,IAAI,CAAC,IAAI,EAAE,SAAS,QAAQ,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC;YAsDnD,OAAO;CAetB"}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpTransport = void 0;
4
+ const utils_1 = require("./utils");
5
+ const DEFAULT_RETRIES = 3;
6
+ const RETRY_BASE_DELAY_MS = 1000;
7
+ class HttpTransport {
8
+ constructor(options) {
9
+ this.compressFn = null;
10
+ this.token = options.token;
11
+ this.endpoint = options.endpoint.replace(/\/+$/, "");
12
+ this.compression = options.compression ?? true;
13
+ this.maxRetries = options.retries ?? DEFAULT_RETRIES;
14
+ }
15
+ /**
16
+ * Sets the compression function. This allows node and browser
17
+ * environments to inject their own gzip implementation.
18
+ */
19
+ setCompressFn(fn) {
20
+ this.compressFn = fn;
21
+ }
22
+ /**
23
+ * Sends an array of log entries to the ingestion endpoint with
24
+ * retry logic using exponential backoff.
25
+ */
26
+ async send(logs) {
27
+ const payload = JSON.stringify(logs);
28
+ let body = payload;
29
+ const headers = {
30
+ "Authorization": `Bearer ${this.token}`,
31
+ "Content-Type": "application/json",
32
+ };
33
+ if (this.compression && this.compressFn) {
34
+ const encoded = new TextEncoder().encode(payload);
35
+ body = await this.compressFn(encoded);
36
+ headers["Content-Encoding"] = "gzip";
37
+ }
38
+ let lastError = null;
39
+ for (let attempt = 0; attempt < this.maxRetries; attempt++) {
40
+ try {
41
+ const response = await this.doFetch(body, headers);
42
+ if (response.status === 202) {
43
+ return response.json;
44
+ }
45
+ if (response.status === 401) {
46
+ throw new Error("Invalid or missing authentication token");
47
+ }
48
+ if (response.status === 400) {
49
+ throw new Error("No valid logs found");
50
+ }
51
+ throw new Error(`Unexpected response status: ${response.status}`);
52
+ }
53
+ catch (err) {
54
+ lastError = err instanceof Error ? err : new Error(String(err));
55
+ // Do not retry auth or validation errors
56
+ if (lastError.message === "Invalid or missing authentication token" ||
57
+ lastError.message === "No valid logs found") {
58
+ throw lastError;
59
+ }
60
+ if (attempt < this.maxRetries - 1) {
61
+ const delay = RETRY_BASE_DELAY_MS * Math.pow(2, attempt);
62
+ await (0, utils_1.sleep)(delay);
63
+ }
64
+ }
65
+ }
66
+ throw lastError ?? new Error("Transport failed after retries");
67
+ }
68
+ async doFetch(body, headers) {
69
+ // Use global fetch (available in Node 18+ and all modern browsers)
70
+ const res = await fetch(`${this.endpoint}`, {
71
+ method: "POST",
72
+ headers,
73
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
+ body: body,
75
+ });
76
+ const json = await res.json().catch(() => ({}));
77
+ return { status: res.status, json };
78
+ }
79
+ }
80
+ exports.HttpTransport = HttpTransport;
81
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Returns the current timestamp in ISO 8601 format.
3
+ */
4
+ export declare function isoTimestamp(): string;
5
+ /**
6
+ * Detects whether the current runtime is a browser environment.
7
+ */
8
+ export declare function isBrowser(): boolean;
9
+ /**
10
+ * Detects whether the current runtime is Node.js.
11
+ */
12
+ export declare function isNode(): boolean;
13
+ /**
14
+ * Waits for the specified number of milliseconds.
15
+ */
16
+ export declare function sleep(ms: number): Promise<void>;
17
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAMnC;AAED;;GAEG;AACH,wBAAgB,MAAM,IAAI,OAAO,CAMhC;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C"}
package/dist/utils.js ADDED
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isoTimestamp = isoTimestamp;
4
+ exports.isBrowser = isBrowser;
5
+ exports.isNode = isNode;
6
+ exports.sleep = sleep;
7
+ /**
8
+ * Returns the current timestamp in ISO 8601 format.
9
+ */
10
+ function isoTimestamp() {
11
+ return new Date().toISOString();
12
+ }
13
+ /**
14
+ * Detects whether the current runtime is a browser environment.
15
+ */
16
+ function isBrowser() {
17
+ return (typeof globalThis !== "undefined" &&
18
+ typeof globalThis.window !== "undefined" &&
19
+ typeof globalThis.document !== "undefined");
20
+ }
21
+ /**
22
+ * Detects whether the current runtime is Node.js.
23
+ */
24
+ function isNode() {
25
+ return (typeof process !== "undefined" &&
26
+ process.versions != null &&
27
+ process.versions.node != null);
28
+ }
29
+ /**
30
+ * Waits for the specified number of milliseconds.
31
+ */
32
+ function sleep(ms) {
33
+ return new Promise((resolve) => setTimeout(resolve, ms));
34
+ }
35
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;AAGA,oCAEC;AAKD,8BAMC;AAKD,wBAMC;AAKD,sBAEC;AAlCD;;GAEG;AACH,SAAgB,YAAY;IAC1B,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,SAAgB,SAAS;IACvB,OAAO,CACL,OAAO,UAAU,KAAK,WAAW;QACjC,OAAQ,UAAsC,CAAC,MAAM,KAAK,WAAW;QACrE,OAAQ,UAAsC,CAAC,QAAQ,KAAK,WAAW,CACxE,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,MAAM;IACpB,OAAO,CACL,OAAO,OAAO,KAAK,WAAW;QAC9B,OAAO,CAAC,QAAQ,IAAI,IAAI;QACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI,CAC9B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,KAAK,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@mihari/logger-core",
3
+ "version": "0.1.0",
4
+ "description": "Core transport, batching, and client logic for mihari",
5
+ "main": "src/index.ts",
6
+ "types": "src/index.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "clean": "rm -rf dist",
13
+ "test": "cd ../.. && npx vitest run --config vitest.config.ts packages/core"
14
+ },
15
+ "dependencies": {
16
+ "@mihari/logger-types": "^0.1.0"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/mihari/mihari-js",
25
+ "directory": "packages/core"
26
+ },
27
+ "gitHead": "dc10a3217caa819965eb3a1e2ff3901a16e510aa"
28
+ }
package/src/index.ts ADDED
@@ -0,0 +1,15 @@
1
+ export { MihariClient } from "./client";
2
+ export { HttpTransport } from "./transport";
3
+ export { Batcher } from "./batcher";
4
+ export type { FlushCallback } from "./batcher";
5
+ export { isoTimestamp, isBrowser, isNode, sleep } from "./utils";
6
+ export {
7
+ LogLevel,
8
+ LogEntry,
9
+ MihariConfig,
10
+ TransportOptions,
11
+ BatchOptions,
12
+ TransportResponse,
13
+ TransportError,
14
+ CompressFn,
15
+ } from "@mihari/logger-types";