logstream-js 0.1.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 +51 -0
- package/dist/index.cjs +139 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +27 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +112 -0
- package/dist/index.js.map +1 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# logstream-js
|
|
2
|
+
|
|
3
|
+
Universal JavaScript/TypeScript client for [Logstream](https://logger.borgstein.io).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install logstream-js
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createLogger } from 'logstream-js';
|
|
15
|
+
|
|
16
|
+
const logger = createLogger({
|
|
17
|
+
apiKey: process.env.LOGSTREAM_API_KEY!,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
logger.info('Server started', { port: 3000 });
|
|
21
|
+
logger.error('Unhandled exception', { message: err.message });
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Batching
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
const logger = createLogger({
|
|
28
|
+
apiKey: process.env.LOGSTREAM_API_KEY!,
|
|
29
|
+
batch: {
|
|
30
|
+
maxSize: 20, // flush after 20 logs (default)
|
|
31
|
+
flushInterval: 5000 // flush every 5 seconds (default)
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Manual flush (e.g. before serverless function exits)
|
|
36
|
+
await logger.flush();
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Config
|
|
40
|
+
|
|
41
|
+
| Option | Type | Default | Description |
|
|
42
|
+
|---|---|---|---|
|
|
43
|
+
| `apiKey` | `string` | — | **Required.** Your app's API key |
|
|
44
|
+
| `baseUrl` | `string` | `https://logger.borgstein.io` | Override for self-hosted |
|
|
45
|
+
| `batch.maxSize` | `number` | `20` | Flush after N queued logs |
|
|
46
|
+
| `batch.flushInterval` | `number` (ms) | `5000` | Flush every N milliseconds |
|
|
47
|
+
| `onError` | `(err: Error) => void` | silent | Called on send failure |
|
|
48
|
+
|
|
49
|
+
## Log levels
|
|
50
|
+
|
|
51
|
+
`debug` · `info` · `warn` · `error`
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
createLogger: () => createLogger
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/client.ts
|
|
28
|
+
var DEFAULT_BASE_URL = "https://logger.borgstein.io";
|
|
29
|
+
var LogstreamClient = class {
|
|
30
|
+
constructor(config) {
|
|
31
|
+
this.apiKey = config.apiKey;
|
|
32
|
+
this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
33
|
+
this.onError = config.onError;
|
|
34
|
+
}
|
|
35
|
+
async send(entry) {
|
|
36
|
+
try {
|
|
37
|
+
const res = await fetch(`${this.baseUrl}/api/logs`, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: {
|
|
40
|
+
"x-api-key": this.apiKey,
|
|
41
|
+
"Content-Type": "application/json"
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify(entry)
|
|
44
|
+
});
|
|
45
|
+
if (!res.ok) {
|
|
46
|
+
throw new Error(`Logstream error: ${res.status}`);
|
|
47
|
+
}
|
|
48
|
+
} catch (err) {
|
|
49
|
+
if (this.onError) {
|
|
50
|
+
this.onError(err instanceof Error ? err : new Error(String(err)));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// src/batch.ts
|
|
57
|
+
var BatchQueue = class {
|
|
58
|
+
constructor(send, options) {
|
|
59
|
+
this.queue = [];
|
|
60
|
+
this.send = send;
|
|
61
|
+
this.maxSize = options.maxSize;
|
|
62
|
+
this.timer = setInterval(() => {
|
|
63
|
+
void this.flush();
|
|
64
|
+
}, options.flushInterval);
|
|
65
|
+
}
|
|
66
|
+
enqueue(entry) {
|
|
67
|
+
this.queue.push(entry);
|
|
68
|
+
if (this.queue.length >= this.maxSize) {
|
|
69
|
+
void this.flush();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async flush() {
|
|
73
|
+
const entries = this.queue.splice(0);
|
|
74
|
+
await Promise.all(entries.map((e) => this.send(e)));
|
|
75
|
+
}
|
|
76
|
+
stop() {
|
|
77
|
+
clearInterval(this.timer);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// src/logger.ts
|
|
82
|
+
var LogstreamLogger = class {
|
|
83
|
+
constructor(config) {
|
|
84
|
+
this.client = new LogstreamClient(config);
|
|
85
|
+
if (config.batch) {
|
|
86
|
+
this.queue = new BatchQueue(
|
|
87
|
+
(entry) => this.client.send(entry),
|
|
88
|
+
{
|
|
89
|
+
maxSize: config.batch.maxSize ?? 20,
|
|
90
|
+
flushInterval: config.batch.flushInterval ?? 5e3
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
log(level, message, metadata) {
|
|
96
|
+
const entry = { level, message, metadata };
|
|
97
|
+
if (this.queue) {
|
|
98
|
+
this.queue.enqueue(entry);
|
|
99
|
+
} else {
|
|
100
|
+
void this.client.send(entry);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
debug(message, metadata) {
|
|
104
|
+
this.log("DEBUG", message, metadata);
|
|
105
|
+
}
|
|
106
|
+
info(message, metadata) {
|
|
107
|
+
this.log("INFO", message, metadata);
|
|
108
|
+
}
|
|
109
|
+
warn(message, metadata) {
|
|
110
|
+
this.log("WARN", message, metadata);
|
|
111
|
+
}
|
|
112
|
+
error(message, metadata) {
|
|
113
|
+
this.log("ERROR", message, metadata);
|
|
114
|
+
}
|
|
115
|
+
async flush() {
|
|
116
|
+
if (this.queue) await this.queue.flush();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// src/index.ts
|
|
121
|
+
function createLogger(config) {
|
|
122
|
+
const logger = new LogstreamLogger(config);
|
|
123
|
+
if (typeof window !== "undefined") {
|
|
124
|
+
window.addEventListener("beforeunload", () => {
|
|
125
|
+
void logger.flush();
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
if (typeof process !== "undefined" && typeof process.on === "function") {
|
|
129
|
+
process.on("beforeExit", () => {
|
|
130
|
+
void logger.flush();
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return logger;
|
|
134
|
+
}
|
|
135
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
136
|
+
0 && (module.exports = {
|
|
137
|
+
createLogger
|
|
138
|
+
});
|
|
139
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/batch.ts","../src/logger.ts"],"sourcesContent":["import { LogstreamLogger } from './logger.js';\nimport type { Logger, LogstreamConfig, LogLevel, LogEntry } from './types.js';\n\nexport function createLogger(config: LogstreamConfig): Logger {\n const logger = new LogstreamLogger(config);\n\n if (typeof window !== 'undefined') {\n window.addEventListener('beforeunload', () => { void logger.flush(); });\n }\n\n if (typeof process !== 'undefined' && typeof process.on === 'function') {\n process.on('beforeExit', () => { void logger.flush(); });\n }\n\n return logger;\n}\n\nexport type { Logger, LogstreamConfig, LogLevel, LogEntry };\n","import type { LogEntry, LogstreamConfig } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://logger.borgstein.io';\n\nexport class LogstreamClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly onError?: (error: Error) => void;\n\n constructor(config: Pick<LogstreamConfig, 'apiKey' | 'baseUrl' | 'onError'>) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n this.onError = config.onError;\n }\n\n async send(entry: LogEntry): Promise<void> {\n try {\n const res = await fetch(`${this.baseUrl}/api/logs`, {\n method: 'POST',\n headers: {\n 'x-api-key': this.apiKey,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(entry),\n });\n if (!res.ok) {\n throw new Error(`Logstream error: ${res.status}`);\n }\n } catch (err) {\n if (this.onError) {\n this.onError(err instanceof Error ? err : new Error(String(err)));\n }\n }\n }\n}\n","import type { LogEntry } from './types.js';\n\ninterface BatchOptions {\n maxSize: number;\n flushInterval: number;\n}\n\nexport class BatchQueue {\n private queue: LogEntry[] = [];\n private timer: ReturnType<typeof setInterval>;\n private readonly send: (entry: LogEntry) => Promise<void>;\n private readonly maxSize: number;\n\n constructor(send: (entry: LogEntry) => Promise<void>, options: BatchOptions) {\n this.send = send;\n this.maxSize = options.maxSize;\n this.timer = setInterval(() => { void this.flush(); }, options.flushInterval);\n }\n\n enqueue(entry: LogEntry): void {\n this.queue.push(entry);\n if (this.queue.length >= this.maxSize) {\n void this.flush();\n }\n }\n\n async flush(): Promise<void> {\n const entries = this.queue.splice(0);\n await Promise.all(entries.map((e) => this.send(e)));\n }\n\n stop(): void {\n clearInterval(this.timer);\n }\n}\n","import { LogstreamClient } from './client.js';\nimport { BatchQueue } from './batch.js';\nimport type { Logger, LogstreamConfig, LogEntry } from './types.js';\n\nexport class LogstreamLogger implements Logger {\n private readonly client: LogstreamClient;\n private readonly queue?: BatchQueue;\n\n constructor(config: LogstreamConfig) {\n this.client = new LogstreamClient(config);\n if (config.batch) {\n this.queue = new BatchQueue(\n (entry: LogEntry) => this.client.send(entry),\n {\n maxSize: config.batch.maxSize ?? 20,\n flushInterval: config.batch.flushInterval ?? 5000,\n }\n );\n }\n }\n\n private log(level: LogEntry['level'], message: string, metadata?: Record<string, unknown>): void {\n const entry: LogEntry = { level, message, metadata };\n if (this.queue) {\n this.queue.enqueue(entry);\n } else {\n void this.client.send(entry);\n }\n }\n\n debug(message: string, metadata?: Record<string, unknown>): void { this.log('DEBUG', message, metadata); }\n info(message: string, metadata?: Record<string, unknown>): void { this.log('INFO', message, metadata); }\n warn(message: string, metadata?: Record<string, unknown>): void { this.log('WARN', message, metadata); }\n error(message: string, metadata?: Record<string, unknown>): void { this.log('ERROR', message, metadata); }\n\n async flush(): Promise<void> {\n if (this.queue) await this.queue.flush();\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';
|
|
2
|
+
interface LogEntry {
|
|
3
|
+
level: LogLevel;
|
|
4
|
+
message: string;
|
|
5
|
+
metadata?: Record<string, unknown>;
|
|
6
|
+
timestamp?: string;
|
|
7
|
+
}
|
|
8
|
+
interface LogstreamConfig {
|
|
9
|
+
apiKey: string;
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
batch?: {
|
|
12
|
+
maxSize?: number;
|
|
13
|
+
flushInterval?: number;
|
|
14
|
+
};
|
|
15
|
+
onError?: (error: Error) => void;
|
|
16
|
+
}
|
|
17
|
+
interface Logger {
|
|
18
|
+
debug(message: string, metadata?: Record<string, unknown>): void;
|
|
19
|
+
info(message: string, metadata?: Record<string, unknown>): void;
|
|
20
|
+
warn(message: string, metadata?: Record<string, unknown>): void;
|
|
21
|
+
error(message: string, metadata?: Record<string, unknown>): void;
|
|
22
|
+
flush(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare function createLogger(config: LogstreamConfig): Logger;
|
|
26
|
+
|
|
27
|
+
export { type LogEntry, type LogLevel, type Logger, type LogstreamConfig, createLogger };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';
|
|
2
|
+
interface LogEntry {
|
|
3
|
+
level: LogLevel;
|
|
4
|
+
message: string;
|
|
5
|
+
metadata?: Record<string, unknown>;
|
|
6
|
+
timestamp?: string;
|
|
7
|
+
}
|
|
8
|
+
interface LogstreamConfig {
|
|
9
|
+
apiKey: string;
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
batch?: {
|
|
12
|
+
maxSize?: number;
|
|
13
|
+
flushInterval?: number;
|
|
14
|
+
};
|
|
15
|
+
onError?: (error: Error) => void;
|
|
16
|
+
}
|
|
17
|
+
interface Logger {
|
|
18
|
+
debug(message: string, metadata?: Record<string, unknown>): void;
|
|
19
|
+
info(message: string, metadata?: Record<string, unknown>): void;
|
|
20
|
+
warn(message: string, metadata?: Record<string, unknown>): void;
|
|
21
|
+
error(message: string, metadata?: Record<string, unknown>): void;
|
|
22
|
+
flush(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare function createLogger(config: LogstreamConfig): Logger;
|
|
26
|
+
|
|
27
|
+
export { type LogEntry, type LogLevel, type Logger, type LogstreamConfig, createLogger };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
var DEFAULT_BASE_URL = "https://logger.borgstein.io";
|
|
3
|
+
var LogstreamClient = class {
|
|
4
|
+
constructor(config) {
|
|
5
|
+
this.apiKey = config.apiKey;
|
|
6
|
+
this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
7
|
+
this.onError = config.onError;
|
|
8
|
+
}
|
|
9
|
+
async send(entry) {
|
|
10
|
+
try {
|
|
11
|
+
const res = await fetch(`${this.baseUrl}/api/logs`, {
|
|
12
|
+
method: "POST",
|
|
13
|
+
headers: {
|
|
14
|
+
"x-api-key": this.apiKey,
|
|
15
|
+
"Content-Type": "application/json"
|
|
16
|
+
},
|
|
17
|
+
body: JSON.stringify(entry)
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
throw new Error(`Logstream error: ${res.status}`);
|
|
21
|
+
}
|
|
22
|
+
} catch (err) {
|
|
23
|
+
if (this.onError) {
|
|
24
|
+
this.onError(err instanceof Error ? err : new Error(String(err)));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/batch.ts
|
|
31
|
+
var BatchQueue = class {
|
|
32
|
+
constructor(send, options) {
|
|
33
|
+
this.queue = [];
|
|
34
|
+
this.send = send;
|
|
35
|
+
this.maxSize = options.maxSize;
|
|
36
|
+
this.timer = setInterval(() => {
|
|
37
|
+
void this.flush();
|
|
38
|
+
}, options.flushInterval);
|
|
39
|
+
}
|
|
40
|
+
enqueue(entry) {
|
|
41
|
+
this.queue.push(entry);
|
|
42
|
+
if (this.queue.length >= this.maxSize) {
|
|
43
|
+
void this.flush();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async flush() {
|
|
47
|
+
const entries = this.queue.splice(0);
|
|
48
|
+
await Promise.all(entries.map((e) => this.send(e)));
|
|
49
|
+
}
|
|
50
|
+
stop() {
|
|
51
|
+
clearInterval(this.timer);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// src/logger.ts
|
|
56
|
+
var LogstreamLogger = class {
|
|
57
|
+
constructor(config) {
|
|
58
|
+
this.client = new LogstreamClient(config);
|
|
59
|
+
if (config.batch) {
|
|
60
|
+
this.queue = new BatchQueue(
|
|
61
|
+
(entry) => this.client.send(entry),
|
|
62
|
+
{
|
|
63
|
+
maxSize: config.batch.maxSize ?? 20,
|
|
64
|
+
flushInterval: config.batch.flushInterval ?? 5e3
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
log(level, message, metadata) {
|
|
70
|
+
const entry = { level, message, metadata };
|
|
71
|
+
if (this.queue) {
|
|
72
|
+
this.queue.enqueue(entry);
|
|
73
|
+
} else {
|
|
74
|
+
void this.client.send(entry);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
debug(message, metadata) {
|
|
78
|
+
this.log("DEBUG", message, metadata);
|
|
79
|
+
}
|
|
80
|
+
info(message, metadata) {
|
|
81
|
+
this.log("INFO", message, metadata);
|
|
82
|
+
}
|
|
83
|
+
warn(message, metadata) {
|
|
84
|
+
this.log("WARN", message, metadata);
|
|
85
|
+
}
|
|
86
|
+
error(message, metadata) {
|
|
87
|
+
this.log("ERROR", message, metadata);
|
|
88
|
+
}
|
|
89
|
+
async flush() {
|
|
90
|
+
if (this.queue) await this.queue.flush();
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// src/index.ts
|
|
95
|
+
function createLogger(config) {
|
|
96
|
+
const logger = new LogstreamLogger(config);
|
|
97
|
+
if (typeof window !== "undefined") {
|
|
98
|
+
window.addEventListener("beforeunload", () => {
|
|
99
|
+
void logger.flush();
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (typeof process !== "undefined" && typeof process.on === "function") {
|
|
103
|
+
process.on("beforeExit", () => {
|
|
104
|
+
void logger.flush();
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return logger;
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
createLogger
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/batch.ts","../src/logger.ts","../src/index.ts"],"sourcesContent":["import type { LogEntry, LogstreamConfig } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://logger.borgstein.io';\n\nexport class LogstreamClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly onError?: (error: Error) => void;\n\n constructor(config: Pick<LogstreamConfig, 'apiKey' | 'baseUrl' | 'onError'>) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n this.onError = config.onError;\n }\n\n async send(entry: LogEntry): Promise<void> {\n try {\n const res = await fetch(`${this.baseUrl}/api/logs`, {\n method: 'POST',\n headers: {\n 'x-api-key': this.apiKey,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(entry),\n });\n if (!res.ok) {\n throw new Error(`Logstream error: ${res.status}`);\n }\n } catch (err) {\n if (this.onError) {\n this.onError(err instanceof Error ? err : new Error(String(err)));\n }\n }\n }\n}\n","import type { LogEntry } from './types.js';\n\ninterface BatchOptions {\n maxSize: number;\n flushInterval: number;\n}\n\nexport class BatchQueue {\n private queue: LogEntry[] = [];\n private timer: ReturnType<typeof setInterval>;\n private readonly send: (entry: LogEntry) => Promise<void>;\n private readonly maxSize: number;\n\n constructor(send: (entry: LogEntry) => Promise<void>, options: BatchOptions) {\n this.send = send;\n this.maxSize = options.maxSize;\n this.timer = setInterval(() => { void this.flush(); }, options.flushInterval);\n }\n\n enqueue(entry: LogEntry): void {\n this.queue.push(entry);\n if (this.queue.length >= this.maxSize) {\n void this.flush();\n }\n }\n\n async flush(): Promise<void> {\n const entries = this.queue.splice(0);\n await Promise.all(entries.map((e) => this.send(e)));\n }\n\n stop(): void {\n clearInterval(this.timer);\n }\n}\n","import { LogstreamClient } from './client.js';\nimport { BatchQueue } from './batch.js';\nimport type { Logger, LogstreamConfig, LogEntry } from './types.js';\n\nexport class LogstreamLogger implements Logger {\n private readonly client: LogstreamClient;\n private readonly queue?: BatchQueue;\n\n constructor(config: LogstreamConfig) {\n this.client = new LogstreamClient(config);\n if (config.batch) {\n this.queue = new BatchQueue(\n (entry: LogEntry) => this.client.send(entry),\n {\n maxSize: config.batch.maxSize ?? 20,\n flushInterval: config.batch.flushInterval ?? 5000,\n }\n );\n }\n }\n\n private log(level: LogEntry['level'], message: string, metadata?: Record<string, unknown>): void {\n const entry: LogEntry = { level, message, metadata };\n if (this.queue) {\n this.queue.enqueue(entry);\n } else {\n void this.client.send(entry);\n }\n }\n\n debug(message: string, metadata?: Record<string, unknown>): void { this.log('DEBUG', message, metadata); }\n info(message: string, metadata?: Record<string, unknown>): void { this.log('INFO', message, metadata); }\n warn(message: string, metadata?: Record<string, unknown>): void { this.log('WARN', message, metadata); }\n error(message: string, metadata?: Record<string, unknown>): void { this.log('ERROR', message, metadata); }\n\n async flush(): Promise<void> {\n if (this.queue) await this.queue.flush();\n }\n}\n","import { LogstreamLogger } from './logger.js';\nimport type { Logger, LogstreamConfig, LogLevel, LogEntry } from './types.js';\n\nexport function createLogger(config: LogstreamConfig): Logger {\n const logger = new LogstreamLogger(config);\n\n if (typeof window !== 'undefined') {\n window.addEventListener('beforeunload', () => { void logger.flush(); });\n }\n\n if (typeof process !== 'undefined' && typeof process.on === 'function') {\n process.on('beforeExit', () => { void logger.flush(); });\n }\n\n return logger;\n}\n\nexport type { Logger, LogstreamConfig, LogLevel, LogEntry };\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "logstream-js",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Universal client for the Logstream logging API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"test:watch": "vitest",
|
|
23
|
+
"typecheck": "tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^25.3.0",
|
|
27
|
+
"tsup": "^8.0.0",
|
|
28
|
+
"typescript": "^5.0.0",
|
|
29
|
+
"vitest": "^1.0.0"
|
|
30
|
+
}
|
|
31
|
+
}
|