@abbacchio/transport 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # @abbacchio/transport
2
+
3
+ Node.js log transports for sending logs to [Abbacchio](https://github.com/pekonchan/pino-live) - a real-time log viewer dashboard.
4
+
5
+ Supports **Pino**, **Winston**, **Bunyan**, and **Console**.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @abbacchio/transport
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Pino
16
+
17
+ ```typescript
18
+ import pino from "pino";
19
+
20
+ const logger = pino({
21
+ transport: {
22
+ targets: [
23
+ { target: "pino-pretty" },
24
+ {
25
+ target: "@abbacchio/transport/transports/pino",
26
+ options: {
27
+ url: "http://localhost:4000/api/logs",
28
+ channel: "my-app",
29
+ },
30
+ },
31
+ ],
32
+ },
33
+ });
34
+
35
+ logger.info({ user: "john" }, "User logged in");
36
+ ```
37
+
38
+ ### Winston
39
+
40
+ ```typescript
41
+ import winston from "winston";
42
+ import { winstonTransport } from "@abbacchio/transport/transports/winston";
43
+
44
+ const logger = winston.createLogger({
45
+ transports: [
46
+ new winston.transports.Console(),
47
+ winstonTransport({
48
+ url: "http://localhost:4000/api/logs",
49
+ channel: "my-app",
50
+ }),
51
+ ],
52
+ });
53
+
54
+ logger.info("User logged in", { user: "john" });
55
+ ```
56
+
57
+ ### Bunyan
58
+
59
+ ```typescript
60
+ import bunyan from "bunyan";
61
+ import { bunyanStream } from "@abbacchio/transport/transports/bunyan";
62
+
63
+ const logger = bunyan.createLogger({
64
+ name: "myapp",
65
+ streams: [
66
+ { stream: process.stdout },
67
+ bunyanStream({
68
+ url: "http://localhost:4000/api/logs",
69
+ channel: "my-app",
70
+ }),
71
+ ],
72
+ });
73
+
74
+ logger.info({ user: "john" }, "User logged in");
75
+ ```
76
+
77
+ ### Console
78
+
79
+ ```typescript
80
+ import { interceptConsole, restoreConsole } from "@abbacchio/transport/transports/console";
81
+
82
+ interceptConsole({
83
+ url: "http://localhost:4000/api/logs",
84
+ channel: "my-app",
85
+ passthrough: true,
86
+ });
87
+
88
+ console.log("This will be sent to Abbacchio!");
89
+
90
+ restoreConsole();
91
+ ```
92
+
93
+ ## Options
94
+
95
+ | Option | Type | Default | Description |
96
+ | ----------- | ------ | -------------------------------- | --------------------------------------- |
97
+ | `url` | string | `http://localhost:4000/api/logs` | Abbacchio server URL |
98
+ | `channel` | string | `default` | Channel name for multi-app support |
99
+ | `secretKey` | string | - | Encryption key (enables E2E encryption) |
100
+ | `batchSize` | number | `10` | Send batch when this many logs accumulate |
101
+ | `interval` | number | `1000` | Send batch after this many ms |
102
+ | `headers` | object | `{}` | Additional HTTP headers |
103
+
104
+ ## End-to-End Encryption
105
+
106
+ Add `secretKey` to encrypt logs before sending:
107
+
108
+ ```typescript
109
+ {
110
+ target: "@abbacchio/transport/transports/pino",
111
+ options: {
112
+ url: "http://localhost:4000/api/logs",
113
+ channel: "my-app",
114
+ secretKey: process.env.LOG_SECRET_KEY,
115
+ },
116
+ }
117
+ ```
118
+
119
+ ## License
120
+
121
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abbacchio/transport",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Node.js log transports for Pino, Winston, and Bunyan - send logs to Abbacchio",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -10,8 +10,9 @@
10
10
  "types": "./dist/index.d.ts",
11
11
  "default": "./dist/index.js"
12
12
  },
13
- "./transports/pino": {
13
+ "./pino": {
14
14
  "types": "./dist/transports/pino.d.ts",
15
+ "require": "./pino-transport.cjs",
15
16
  "default": "./dist/transports/pino.js"
16
17
  },
17
18
  "./transports/winston": {
@@ -46,11 +47,24 @@
46
47
  "pino-abstract-transport": "^3.0.0",
47
48
  "winston-transport": "^4.9.0"
48
49
  },
50
+ "peerDependencies": {
51
+ "pino": "^8.0.0 || ^9.0.0"
52
+ },
53
+ "peerDependenciesMeta": {
54
+ "pino": {
55
+ "optional": true
56
+ }
57
+ },
49
58
  "devDependencies": {
50
59
  "@types/node": "^22.10.5",
60
+ "pino": "^9.0.0",
51
61
  "typescript": "^5.7.2"
52
62
  },
53
63
  "engines": {
54
64
  "node": ">=18"
65
+ },
66
+ "repository": {
67
+ "type": "git",
68
+ "url": "https://github.com/mood-agency/abbacchio"
55
69
  }
56
70
  }
@@ -0,0 +1,30 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * CommonJS wrapper for the Pino transport.
5
+ *
6
+ * Pino runs transports in worker threads using require(), which doesn't
7
+ * work well with ESM subpath exports. This CJS wrapper allows users to
8
+ * simply use: target: '@abbacchio/transport'
9
+ *
10
+ * @example
11
+ * ```javascript
12
+ * const pino = require('pino');
13
+ *
14
+ * const logger = pino({
15
+ * transport: {
16
+ * target: '@abbacchio/transport',
17
+ * options: {
18
+ * url: 'http://localhost:4000/api/logs',
19
+ * channel: 'my-app',
20
+ * },
21
+ * },
22
+ * });
23
+ * ```
24
+ */
25
+
26
+ // Dynamic import the ESM module
27
+ module.exports = async function(opts) {
28
+ const { default: pinoTransport } = await import('./dist/transports/pino.js');
29
+ return pinoTransport(opts);
30
+ };
package/src/client.ts CHANGED
@@ -1,148 +1,148 @@
1
- import { encrypt } from "./encrypt.js";
2
-
3
- export interface AbbacchioClientOptions {
4
- /** Server URL endpoint */
5
- url?: string;
6
- /** Secret key for encryption. If provided, logs will be encrypted before sending */
7
- secretKey?: string;
8
- /** Channel/app name for multi-app support. Defaults to 'default' */
9
- channel?: string;
10
- /** Number of logs to batch before sending. Defaults to 10 */
11
- batchSize?: number;
12
- /** Interval in ms between flushes. Defaults to 1000 */
13
- interval?: number;
14
- /** Additional headers to send with requests */
15
- headers?: Record<string, string>;
16
- }
17
-
18
- /**
19
- * Shared HTTP client for all Abbacchio transports.
20
- * Handles batching, encryption, and HTTP communication.
21
- */
22
- export class AbbacchioClient {
23
- private url: string;
24
- private secretKey?: string;
25
- private channel?: string;
26
- private batchSize: number;
27
- private interval: number;
28
- private headers: Record<string, string>;
29
-
30
- private buffer: unknown[] = [];
31
- private timer: ReturnType<typeof setTimeout> | null = null;
32
-
33
- constructor(options: AbbacchioClientOptions = {}) {
34
- this.url = options.url || "http://localhost:4000/api/logs";
35
- this.secretKey = options.secretKey;
36
- this.channel = options.channel;
37
- this.batchSize = options.batchSize || 10;
38
- this.interval = options.interval || 1000;
39
- this.headers = options.headers || {};
40
- }
41
-
42
- /**
43
- * Process a log entry (encrypt if secretKey is provided)
44
- */
45
- private processLog(log: unknown): unknown {
46
- if (this.secretKey) {
47
- return { encrypted: encrypt(JSON.stringify(log), this.secretKey) };
48
- }
49
- return log;
50
- }
51
-
52
- /**
53
- * Add a log to the buffer and trigger send if needed
54
- */
55
- add(log: unknown): void {
56
- this.buffer.push(this.processLog(log));
57
-
58
- if (this.buffer.length >= this.batchSize) {
59
- this.flush();
60
- } else {
61
- this.scheduleSend();
62
- }
63
- }
64
-
65
- /**
66
- * Add multiple logs at once
67
- */
68
- addBatch(logs: unknown[]): void {
69
- for (const log of logs) {
70
- this.buffer.push(this.processLog(log));
71
- }
72
-
73
- if (this.buffer.length >= this.batchSize) {
74
- this.flush();
75
- } else {
76
- this.scheduleSend();
77
- }
78
- }
79
-
80
- /**
81
- * Send logs immediately without batching
82
- */
83
- async send(logs: unknown[]): Promise<void> {
84
- const processedLogs = logs.map(log => this.processLog(log));
85
- await this.sendToServer(processedLogs);
86
- }
87
-
88
- /**
89
- * Schedule a send after the interval
90
- */
91
- private scheduleSend(): void {
92
- if (this.timer) return;
93
- this.timer = setTimeout(() => {
94
- this.timer = null;
95
- this.flush();
96
- }, this.interval);
97
- }
98
-
99
- /**
100
- * Flush the buffer and send to server
101
- */
102
- async flush(): Promise<void> {
103
- if (this.buffer.length === 0) return;
104
-
105
- const toSend = this.buffer;
106
- this.buffer = [];
107
-
108
- await this.sendToServer(toSend);
109
- }
110
-
111
- /**
112
- * Send logs to the Abbacchio server
113
- */
114
- private async sendToServer(logs: unknown[]): Promise<void> {
115
- try {
116
- await fetch(this.url, {
117
- method: "POST",
118
- headers: {
119
- "Content-Type": "application/json",
120
- "X-Encrypted": this.secretKey ? "true" : "false",
121
- ...(this.channel ? { "X-Channel": this.channel } : {}),
122
- ...this.headers,
123
- },
124
- body: JSON.stringify({ logs }),
125
- });
126
- } catch {
127
- // Silently fail - don't break the app if Abbacchio server is down
128
- }
129
- }
130
-
131
- /**
132
- * Close the client and flush any remaining logs
133
- */
134
- async close(): Promise<void> {
135
- if (this.timer) {
136
- clearTimeout(this.timer);
137
- this.timer = null;
138
- }
139
- await this.flush();
140
- }
141
- }
142
-
143
- /**
144
- * Create a new Abbacchio client instance
145
- */
146
- export function createClient(options?: AbbacchioClientOptions): AbbacchioClient {
147
- return new AbbacchioClient(options);
148
- }
1
+ import { encrypt } from "./encrypt.js";
2
+
3
+ export interface AbbacchioClientOptions {
4
+ /** Server URL endpoint */
5
+ url?: string;
6
+ /** Secret key for encryption. If provided, logs will be encrypted before sending */
7
+ secretKey?: string;
8
+ /** Channel/app name for multi-app support. Defaults to 'default' */
9
+ channel?: string;
10
+ /** Number of logs to batch before sending. Defaults to 10 */
11
+ batchSize?: number;
12
+ /** Interval in ms between flushes. Defaults to 1000 */
13
+ interval?: number;
14
+ /** Additional headers to send with requests */
15
+ headers?: Record<string, string>;
16
+ }
17
+
18
+ /**
19
+ * Shared HTTP client for all Abbacchio transports.
20
+ * Handles batching, encryption, and HTTP communication.
21
+ */
22
+ export class AbbacchioClient {
23
+ private url: string;
24
+ private secretKey?: string;
25
+ private channel?: string;
26
+ private batchSize: number;
27
+ private interval: number;
28
+ private headers: Record<string, string>;
29
+
30
+ private buffer: unknown[] = [];
31
+ private timer: ReturnType<typeof setTimeout> | null = null;
32
+
33
+ constructor(options: AbbacchioClientOptions = {}) {
34
+ this.url = options.url || "http://localhost:4000/api/logs";
35
+ this.secretKey = options.secretKey;
36
+ this.channel = options.channel;
37
+ this.batchSize = options.batchSize || 10;
38
+ this.interval = options.interval || 1000;
39
+ this.headers = options.headers || {};
40
+ }
41
+
42
+ /**
43
+ * Process a log entry (encrypt if secretKey is provided)
44
+ */
45
+ private processLog(log: unknown): unknown {
46
+ if (this.secretKey) {
47
+ return { encrypted: encrypt(JSON.stringify(log), this.secretKey) };
48
+ }
49
+ return log;
50
+ }
51
+
52
+ /**
53
+ * Add a log to the buffer and trigger send if needed
54
+ */
55
+ add(log: unknown): void {
56
+ this.buffer.push(this.processLog(log));
57
+
58
+ if (this.buffer.length >= this.batchSize) {
59
+ this.flush();
60
+ } else {
61
+ this.scheduleSend();
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Add multiple logs at once
67
+ */
68
+ addBatch(logs: unknown[]): void {
69
+ for (const log of logs) {
70
+ this.buffer.push(this.processLog(log));
71
+ }
72
+
73
+ if (this.buffer.length >= this.batchSize) {
74
+ this.flush();
75
+ } else {
76
+ this.scheduleSend();
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Send logs immediately without batching
82
+ */
83
+ async send(logs: unknown[]): Promise<void> {
84
+ const processedLogs = logs.map(log => this.processLog(log));
85
+ await this.sendToServer(processedLogs);
86
+ }
87
+
88
+ /**
89
+ * Schedule a send after the interval
90
+ */
91
+ private scheduleSend(): void {
92
+ if (this.timer) return;
93
+ this.timer = setTimeout(() => {
94
+ this.timer = null;
95
+ this.flush();
96
+ }, this.interval);
97
+ }
98
+
99
+ /**
100
+ * Flush the buffer and send to server
101
+ */
102
+ async flush(): Promise<void> {
103
+ if (this.buffer.length === 0) return;
104
+
105
+ const toSend = this.buffer;
106
+ this.buffer = [];
107
+
108
+ await this.sendToServer(toSend);
109
+ }
110
+
111
+ /**
112
+ * Send logs to the Abbacchio server
113
+ */
114
+ private async sendToServer(logs: unknown[]): Promise<void> {
115
+ try {
116
+ await fetch(this.url, {
117
+ method: "POST",
118
+ headers: {
119
+ "Content-Type": "application/json",
120
+ "X-Encrypted": this.secretKey ? "true" : "false",
121
+ ...(this.channel ? { "X-Channel": this.channel } : {}),
122
+ ...this.headers,
123
+ },
124
+ body: JSON.stringify({ logs }),
125
+ });
126
+ } catch {
127
+ // Silently fail - don't break the app if Abbacchio server is down
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Close the client and flush any remaining logs
133
+ */
134
+ async close(): Promise<void> {
135
+ if (this.timer) {
136
+ clearTimeout(this.timer);
137
+ this.timer = null;
138
+ }
139
+ await this.flush();
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Create a new Abbacchio client instance
145
+ */
146
+ export function createClient(options?: AbbacchioClientOptions): AbbacchioClient {
147
+ return new AbbacchioClient(options);
148
+ }