@hardlydifficult/logger 1.0.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/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # @hardlydifficult/logger
2
+
3
+ Plugin-based structured logger with Console, Discord, and File output plugins.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @hardlydifficult/logger
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { Logger, ConsolePlugin, FilePlugin, DiscordPlugin } from "@hardlydifficult/logger";
15
+
16
+ const discord = new DiscordPlugin();
17
+
18
+ const logger = new Logger("info")
19
+ .use(new ConsolePlugin())
20
+ .use(new FilePlugin("/var/log/app.log"))
21
+ .use(discord);
22
+
23
+ // Wire up Discord sender once the bot is ready
24
+ discord.setSender((msg) => channel.send(msg));
25
+
26
+ logger.info("Server started", { port: 3000 });
27
+ logger.warn("High memory usage", { usage: "85%" });
28
+ logger.error("Request failed", { url: "/api/data", status: 500 });
29
+
30
+ // Out-of-band notification (goes to plugins that support notify)
31
+ logger.notify("Deployment complete");
32
+ ```
33
+
34
+ ## API
35
+
36
+ ### `Logger`
37
+
38
+ ```typescript
39
+ new Logger(minLevel?: LogLevel) // default: "info"
40
+ ```
41
+
42
+ | Method | Description |
43
+ |--------|-------------|
44
+ | `use(plugin)` | Register a plugin (returns `this` for chaining) |
45
+ | `debug(message, context?)` | Log at debug level |
46
+ | `info(message, context?)` | Log at info level |
47
+ | `warn(message, context?)` | Log at warn level |
48
+ | `error(message, context?)` | Log at error level |
49
+ | `notify(message)` | Send out-of-band notification to plugins that support it |
50
+
51
+ Log levels: `debug` < `info` < `warn` < `error`. Entries below `minLevel` are filtered out.
52
+
53
+ ### Plugins
54
+
55
+ #### `ConsolePlugin`
56
+
57
+ Logs to `console.log`/`console.warn`/`console.error` with formatted timestamps.
58
+
59
+ ```typescript
60
+ new ConsolePlugin()
61
+ ```
62
+
63
+ #### `FilePlugin`
64
+
65
+ Appends JSON log entries to a file (one entry per line). Creates the directory if needed.
66
+
67
+ ```typescript
68
+ new FilePlugin(filePath: string)
69
+ ```
70
+
71
+ #### `DiscordPlugin`
72
+
73
+ Sends warn/error logs and notifications to Discord. The sender is set lazily since the Discord connection may not be ready at logger creation time.
74
+
75
+ ```typescript
76
+ const discord = new DiscordPlugin();
77
+ discord.setSender((msg) => channel.send(msg));
78
+ ```
79
+
80
+ ### Custom Plugins
81
+
82
+ Implement the `LoggerPlugin` interface:
83
+
84
+ ```typescript
85
+ import type { LoggerPlugin, LogEntry } from "@hardlydifficult/logger";
86
+
87
+ class MyPlugin implements LoggerPlugin {
88
+ log(entry: LogEntry): void {
89
+ // Handle log entry
90
+ }
91
+
92
+ // Optional: handle notify() calls
93
+ notify?(message: string): void {
94
+ // Handle notification
95
+ }
96
+ }
97
+
98
+ logger.use(new MyPlugin());
99
+ ```
100
+
101
+ ### Types
102
+
103
+ ```typescript
104
+ type LogLevel = "debug" | "info" | "warn" | "error";
105
+
106
+ interface LogEntry {
107
+ readonly level: LogLevel;
108
+ readonly message: string;
109
+ readonly timestamp: string;
110
+ readonly context?: Readonly<Record<string, unknown>>;
111
+ }
112
+
113
+ interface LoggerPlugin {
114
+ log(entry: LogEntry): void;
115
+ notify?(message: string): void;
116
+ }
117
+ ```
@@ -0,0 +1,15 @@
1
+ import type { LoggerPlugin, LogLevel } from "./types.js";
2
+ export declare class Logger {
3
+ private readonly minLevel;
4
+ private readonly plugins;
5
+ constructor(minLevel?: LogLevel);
6
+ use(plugin: LoggerPlugin): this;
7
+ debug(message: string, context?: Readonly<Record<string, unknown>>): void;
8
+ info(message: string, context?: Readonly<Record<string, unknown>>): void;
9
+ warn(message: string, context?: Readonly<Record<string, unknown>>): void;
10
+ error(message: string, context?: Readonly<Record<string, unknown>>): void;
11
+ notify(message: string): void;
12
+ private shouldLog;
13
+ private log;
14
+ }
15
+ //# sourceMappingURL=Logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Logger.d.ts","sourceRoot":"","sources":["../src/Logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AASnE,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;gBAElC,QAAQ,GAAE,QAAiB;IAIvC,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAK/B,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI;IAIzE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI;IAIxE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI;IAIxE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI;IAIzE,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAY7B,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,GAAG;CAsBZ"}
package/dist/Logger.js ADDED
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Logger = void 0;
4
+ const LOG_LEVELS = {
5
+ debug: 0,
6
+ info: 1,
7
+ warn: 2,
8
+ error: 3,
9
+ };
10
+ class Logger {
11
+ minLevel;
12
+ plugins = [];
13
+ constructor(minLevel = "info") {
14
+ this.minLevel = minLevel;
15
+ }
16
+ use(plugin) {
17
+ this.plugins.push(plugin);
18
+ return this;
19
+ }
20
+ debug(message, context) {
21
+ this.log("debug", message, context);
22
+ }
23
+ info(message, context) {
24
+ this.log("info", message, context);
25
+ }
26
+ warn(message, context) {
27
+ this.log("warn", message, context);
28
+ }
29
+ error(message, context) {
30
+ this.log("error", message, context);
31
+ }
32
+ notify(message) {
33
+ for (const plugin of this.plugins) {
34
+ if (plugin.notify) {
35
+ try {
36
+ plugin.notify(message);
37
+ }
38
+ catch {
39
+ /* swallow */
40
+ }
41
+ }
42
+ }
43
+ }
44
+ shouldLog(level) {
45
+ return LOG_LEVELS[level] >= LOG_LEVELS[this.minLevel];
46
+ }
47
+ log(level, message, context) {
48
+ if (!this.shouldLog(level)) {
49
+ return;
50
+ }
51
+ const entry = context !== undefined
52
+ ? { level, message, timestamp: new Date().toISOString(), context }
53
+ : { level, message, timestamp: new Date().toISOString() };
54
+ for (const plugin of this.plugins) {
55
+ try {
56
+ plugin.log(entry);
57
+ }
58
+ catch {
59
+ /* swallow */
60
+ }
61
+ }
62
+ }
63
+ }
64
+ exports.Logger = Logger;
65
+ //# sourceMappingURL=Logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Logger.js","sourceRoot":"","sources":["../src/Logger.ts"],"names":[],"mappings":";;;AAEA,MAAM,UAAU,GAAuC;IACrD,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AAEF,MAAa,MAAM;IACA,QAAQ,CAAW;IACnB,OAAO,GAAmB,EAAE,CAAC;IAE9C,YAAY,WAAqB,MAAM;QACrC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,GAAG,CAAC,MAAoB;QACtB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,OAA2C;QAChE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,OAA2C;QAC/D,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,OAA2C;QAC/D,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,OAA2C;QAChE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,CAAC,OAAe;QACpB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,IAAI,CAAC;oBACH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBACzB,CAAC;gBAAC,MAAM,CAAC;oBACP,aAAa;gBACf,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAEO,SAAS,CAAC,KAAe;QAC/B,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxD,CAAC;IAEO,GAAG,CACT,KAAe,EACf,OAAe,EACf,OAA2C;QAE3C,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GACT,OAAO,KAAK,SAAS;YACnB,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE;YAClE,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;QAE9D,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACpB,CAAC;YAAC,MAAM,CAAC;gBACP,aAAa;YACf,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAnED,wBAmEC"}
@@ -0,0 +1,6 @@
1
+ export { Logger } from "./Logger.js";
2
+ export { ConsolePlugin, formatEntry } from "./plugins/ConsolePlugin.js";
3
+ export { DiscordPlugin, type DiscordSender } from "./plugins/DiscordPlugin.js";
4
+ export { FilePlugin } from "./plugins/FilePlugin.js";
5
+ export type { LogLevel, LogEntry, LoggerPlugin } from "./types.js";
6
+ //# 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,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FilePlugin = exports.DiscordPlugin = exports.formatEntry = exports.ConsolePlugin = exports.Logger = void 0;
4
+ var Logger_js_1 = require("./Logger.js");
5
+ Object.defineProperty(exports, "Logger", { enumerable: true, get: function () { return Logger_js_1.Logger; } });
6
+ var ConsolePlugin_js_1 = require("./plugins/ConsolePlugin.js");
7
+ Object.defineProperty(exports, "ConsolePlugin", { enumerable: true, get: function () { return ConsolePlugin_js_1.ConsolePlugin; } });
8
+ Object.defineProperty(exports, "formatEntry", { enumerable: true, get: function () { return ConsolePlugin_js_1.formatEntry; } });
9
+ var DiscordPlugin_js_1 = require("./plugins/DiscordPlugin.js");
10
+ Object.defineProperty(exports, "DiscordPlugin", { enumerable: true, get: function () { return DiscordPlugin_js_1.DiscordPlugin; } });
11
+ var FilePlugin_js_1 = require("./plugins/FilePlugin.js");
12
+ Object.defineProperty(exports, "FilePlugin", { enumerable: true, get: function () { return FilePlugin_js_1.FilePlugin; } });
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yCAAqC;AAA5B,mGAAA,MAAM,OAAA;AACf,+DAAwE;AAA/D,iHAAA,aAAa,OAAA;AAAE,+GAAA,WAAW,OAAA;AACnC,+DAA+E;AAAtE,iHAAA,aAAa,OAAA;AACtB,yDAAqD;AAA5C,2GAAA,UAAU,OAAA"}
@@ -0,0 +1,6 @@
1
+ import type { LogEntry, LoggerPlugin } from "../types.js";
2
+ export declare function formatEntry(entry: LogEntry): string;
3
+ export declare class ConsolePlugin implements LoggerPlugin {
4
+ log(entry: LogEntry): void;
5
+ }
6
+ //# sourceMappingURL=ConsolePlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConsolePlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/ConsolePlugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE1D,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAMnD;AAED,qBAAa,aAAc,YAAW,YAAY;IAChD,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;CAmB3B"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConsolePlugin = void 0;
4
+ exports.formatEntry = formatEntry;
5
+ function formatEntry(entry) {
6
+ const base = `[${entry.timestamp}] ${entry.level.toUpperCase()}: ${entry.message}`;
7
+ if (entry.context && Object.keys(entry.context).length > 0) {
8
+ return `${base} ${JSON.stringify(entry.context)}`;
9
+ }
10
+ return base;
11
+ }
12
+ class ConsolePlugin {
13
+ log(entry) {
14
+ const formatted = formatEntry(entry);
15
+ switch (entry.level) {
16
+ case "debug":
17
+ case "info":
18
+ // eslint-disable-next-line no-console
19
+ console.log(formatted);
20
+ break;
21
+ case "warn":
22
+ console.warn(formatted);
23
+ break;
24
+ case "error":
25
+ console.error(formatted);
26
+ break;
27
+ default:
28
+ console.warn(formatted);
29
+ break;
30
+ }
31
+ }
32
+ }
33
+ exports.ConsolePlugin = ConsolePlugin;
34
+ //# sourceMappingURL=ConsolePlugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConsolePlugin.js","sourceRoot":"","sources":["../../src/plugins/ConsolePlugin.ts"],"names":[],"mappings":";;;AAEA,kCAMC;AAND,SAAgB,WAAW,CAAC,KAAe;IACzC,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;IACnF,IAAI,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3D,OAAO,GAAG,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;IACpD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAa,aAAa;IACxB,GAAG,CAAC,KAAe;QACjB,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QACrC,QAAQ,KAAK,CAAC,KAAK,EAAE,CAAC;YACpB,KAAK,OAAO,CAAC;YACb,KAAK,MAAM;gBACT,sCAAsC;gBACtC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBACvB,MAAM;YACR,KAAK,MAAM;gBACT,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACxB,MAAM;YACR,KAAK,OAAO;gBACV,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBACzB,MAAM;YACR;gBACE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACxB,MAAM;QACV,CAAC;IACH,CAAC;CACF;AApBD,sCAoBC"}
@@ -0,0 +1,9 @@
1
+ import type { LogEntry, LoggerPlugin } from "../types.js";
2
+ export type DiscordSender = (message: string) => void;
3
+ export declare class DiscordPlugin implements LoggerPlugin {
4
+ private sender;
5
+ setSender(sender: DiscordSender): void;
6
+ log(entry: LogEntry): void;
7
+ notify(message: string): void;
8
+ }
9
+ //# sourceMappingURL=DiscordPlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DiscordPlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/DiscordPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE1D,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAEtD,qBAAa,aAAc,YAAW,YAAY;IAChD,OAAO,CAAC,MAAM,CAA8B;IAE5C,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAItC,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAqB1B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAU9B"}
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DiscordPlugin = void 0;
4
+ class DiscordPlugin {
5
+ sender = null;
6
+ setSender(sender) {
7
+ this.sender = sender;
8
+ }
9
+ log(entry) {
10
+ if (!this.sender) {
11
+ return;
12
+ }
13
+ if (entry.level !== "warn" && entry.level !== "error") {
14
+ return;
15
+ }
16
+ const prefix = entry.level === "error" ? "\u{1f6a8}" : "\u{26a0}\u{fe0f}";
17
+ const discordMessage = entry.context && Object.keys(entry.context).length > 0
18
+ ? `${prefix} **${entry.level.toUpperCase()}**: ${entry.message}\n\`\`\`json\n${JSON.stringify(entry.context, null, 2)}\n\`\`\``
19
+ : `${prefix} **${entry.level.toUpperCase()}**: ${entry.message}`;
20
+ try {
21
+ this.sender(discordMessage);
22
+ }
23
+ catch {
24
+ /* swallow */
25
+ }
26
+ }
27
+ notify(message) {
28
+ if (!this.sender) {
29
+ return;
30
+ }
31
+ try {
32
+ this.sender(message);
33
+ }
34
+ catch {
35
+ /* swallow */
36
+ }
37
+ }
38
+ }
39
+ exports.DiscordPlugin = DiscordPlugin;
40
+ //# sourceMappingURL=DiscordPlugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DiscordPlugin.js","sourceRoot":"","sources":["../../src/plugins/DiscordPlugin.ts"],"names":[],"mappings":";;;AAIA,MAAa,aAAa;IAChB,MAAM,GAAyB,IAAI,CAAC;IAE5C,SAAS,CAAC,MAAqB;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,GAAG,CAAC,KAAe;QACjB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;YACtD,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,kBAAkB,CAAC;QAC1E,MAAM,cAAc,GAClB,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC;YACpD,CAAC,CAAC,GAAG,MAAM,MAAM,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,OAAO,KAAK,CAAC,OAAO,iBAAiB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,UAAU;YAC/H,CAAC,CAAC,GAAG,MAAM,MAAM,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,OAAO,KAAK,CAAC,OAAO,EAAE,CAAC;QAErE,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,aAAa;QACf,CAAC;IACH,CAAC;IAED,MAAM,CAAC,OAAe;QACpB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,aAAa;QACf,CAAC;IACH,CAAC;CACF;AAtCD,sCAsCC"}
@@ -0,0 +1,7 @@
1
+ import type { LogEntry, LoggerPlugin } from "../types.js";
2
+ export declare class FilePlugin implements LoggerPlugin {
3
+ private readonly filePath;
4
+ constructor(filePath: string);
5
+ log(entry: LogEntry): void;
6
+ }
7
+ //# sourceMappingURL=FilePlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilePlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/FilePlugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE1D,qBAAa,UAAW,YAAW,YAAY;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,QAAQ,EAAE,MAAM;IAK5B,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;CAQ3B"}
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FilePlugin = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ class FilePlugin {
7
+ filePath;
8
+ constructor(filePath) {
9
+ this.filePath = filePath;
10
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(filePath), { recursive: true });
11
+ }
12
+ log(entry) {
13
+ const line = `${JSON.stringify(entry)}\n`;
14
+ try {
15
+ (0, node_fs_1.appendFileSync)(this.filePath, line);
16
+ }
17
+ catch {
18
+ /* swallow */
19
+ }
20
+ }
21
+ }
22
+ exports.FilePlugin = FilePlugin;
23
+ //# sourceMappingURL=FilePlugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilePlugin.js","sourceRoot":"","sources":["../../src/plugins/FilePlugin.ts"],"names":[],"mappings":";;;AAAA,qCAAoD;AACpD,yCAAoC;AAIpC,MAAa,UAAU;IACJ,QAAQ,CAAS;IAElC,YAAY,QAAgB;QAC1B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAA,mBAAS,EAAC,IAAA,mBAAO,EAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,GAAG,CAAC,KAAe;QACjB,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;QAC1C,IAAI,CAAC;YACH,IAAA,wBAAc,EAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,aAAa;QACf,CAAC;IACH,CAAC;CACF;AAhBD,gCAgBC"}
@@ -0,0 +1,14 @@
1
+ export type LogLevel = "debug" | "info" | "warn" | "error";
2
+ export interface LogEntry {
3
+ readonly level: LogLevel;
4
+ readonly message: string;
5
+ readonly timestamp: string;
6
+ readonly context?: Readonly<Record<string, unknown>>;
7
+ }
8
+ export interface LoggerPlugin {
9
+ /** Called for each log entry that passes the level filter. */
10
+ log(entry: LogEntry): void;
11
+ /** Called for notify() — out-of-band notifications. Optional. */
12
+ notify?(message: string): void;
13
+ }
14
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,YAAY;IAC3B,8DAA8D;IAC9D,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,iEAAiE;IACjE,MAAM,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@hardlydifficult/logger",
3
+ "version": "1.0.0",
4
+ "main": "./dist/index.js",
5
+ "types": "./dist/index.d.ts",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "test": "vitest run",
12
+ "test:watch": "vitest",
13
+ "test:coverage": "vitest run --coverage",
14
+ "lint": "tsc --noEmit",
15
+ "clean": "rm -rf dist"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "20.19.31",
19
+ "typescript": "5.8.3",
20
+ "vitest": "1.6.1"
21
+ },
22
+ "engines": {
23
+ "node": ">=18.0.0"
24
+ }
25
+ }