logora-websocket 1.0.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/LICENSE +21 -0
- package/README.md +290 -0
- package/dist/index.d.mts +110 -0
- package/dist/index.d.ts +110 -0
- package/dist/index.js +286 -0
- package/dist/index.mjs +257 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 boseba
|
|
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,290 @@
|
|
|
1
|
+
# logora-websocket
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/logora-websocket)
|
|
4
|
+
[](https://coveralls.io/github/boseba/logora-websocket?branch=main)
|
|
5
|
+
|
|
6
|
+
logora-websocket is the official WebSocket output module for the [Logora](https://www.npmjs.com/package/logora) logging framework.
|
|
7
|
+
|
|
8
|
+
It forwards Logora writer instructions as structured JSON messages through a broadcaster abstraction, making it easy to stream server logs to remote clients, dashboards, or custom live viewers.
|
|
9
|
+
|
|
10
|
+
* * *
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- Structured WebSocket transport for Logora writer instructions
|
|
15
|
+
- Supports `log`, `print`, `title`, `empty`, and `clear`
|
|
16
|
+
- Safe serialization for complex values:
|
|
17
|
+
- `Error`
|
|
18
|
+
- `Date`
|
|
19
|
+
- `BigInt`
|
|
20
|
+
- `Symbol`
|
|
21
|
+
- functions
|
|
22
|
+
- circular references
|
|
23
|
+
- Broadcast-oriented design for live log viewers
|
|
24
|
+
- No built-in server lifecycle logic
|
|
25
|
+
- Compatible with custom broadcasters and `ws`
|
|
26
|
+
- Non-blocking design aligned with the Logora plugin model
|
|
27
|
+
|
|
28
|
+
* * *
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
npm install logora logora-websocket
|
|
33
|
+
|
|
34
|
+
If you want to use the provided `ws` broadcaster helper:
|
|
35
|
+
|
|
36
|
+
npm install ws
|
|
37
|
+
|
|
38
|
+
* * *
|
|
39
|
+
|
|
40
|
+
## Basic Usage
|
|
41
|
+
|
|
42
|
+
import { createServer } from "node:http";
|
|
43
|
+
import { createLogger, LogLevel } from "logora";
|
|
44
|
+
import {
|
|
45
|
+
createWebSocketOutput,
|
|
46
|
+
createWsBroadcaster,
|
|
47
|
+
} from "logora-websocket";
|
|
48
|
+
import { WebSocketServer } from "ws";
|
|
49
|
+
|
|
50
|
+
const httpServer = createServer();
|
|
51
|
+
const webSocketServer = new WebSocketServer({ server: httpServer });
|
|
52
|
+
|
|
53
|
+
const logger = createLogger({ level: LogLevel.Info });
|
|
54
|
+
|
|
55
|
+
logger.addLogOutput(
|
|
56
|
+
createWebSocketOutput({
|
|
57
|
+
broadcaster: createWsBroadcaster(webSocketServer),
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
logger.info("Server started on port {0}", 3000);
|
|
62
|
+
|
|
63
|
+
httpServer.listen(3000);
|
|
64
|
+
|
|
65
|
+
* * *
|
|
66
|
+
|
|
67
|
+
## Scoped Logging
|
|
68
|
+
|
|
69
|
+
Scoped loggers are supported exactly like in the rest of the Logora ecosystem:
|
|
70
|
+
|
|
71
|
+
const apiLogger = logger.getScoped("API");
|
|
72
|
+
|
|
73
|
+
apiLogger.info("Request received: {0}", "/users");
|
|
74
|
+
apiLogger.warning("Rate limit reached for client {0}", clientId);
|
|
75
|
+
|
|
76
|
+
When a scope is present, it is included in the serialized payload.
|
|
77
|
+
|
|
78
|
+
* * *
|
|
79
|
+
|
|
80
|
+
## How It Works
|
|
81
|
+
|
|
82
|
+
logora-websocket does not create or manage a WebSocket server.
|
|
83
|
+
|
|
84
|
+
Your application remains responsible for:
|
|
85
|
+
|
|
86
|
+
- creating the server
|
|
87
|
+
- authenticating clients
|
|
88
|
+
- deciding which clients receive logs
|
|
89
|
+
- closing connections
|
|
90
|
+
- integrating with your HTTP server or framework
|
|
91
|
+
|
|
92
|
+
The module only transforms Logora writer calls into structured JSON messages and forwards them through a `WebSocketBroadcaster`.
|
|
93
|
+
|
|
94
|
+
* * *
|
|
95
|
+
|
|
96
|
+
## Broadcaster Abstraction
|
|
97
|
+
|
|
98
|
+
The transport relies on a very small broadcaster contract:
|
|
99
|
+
|
|
100
|
+
export interface WebSocketBroadcaster {
|
|
101
|
+
broadcast(message: string): void;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
This keeps the module focused and lets you adapt it to your own WebSocket infrastructure.
|
|
105
|
+
|
|
106
|
+
### Example with a custom broadcaster
|
|
107
|
+
|
|
108
|
+
import { createWebSocketOutput } from "logora-websocket";
|
|
109
|
+
|
|
110
|
+
const broadcaster = {
|
|
111
|
+
broadcast(message: string): void {
|
|
112
|
+
myCustomSocketHub.sendToAll(message);
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
logger.addLogOutput(
|
|
117
|
+
createWebSocketOutput({
|
|
118
|
+
broadcaster,
|
|
119
|
+
}),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
* * *
|
|
123
|
+
|
|
124
|
+
## Using the `ws` Broadcaster Helper
|
|
125
|
+
|
|
126
|
+
If your server uses [`ws`](https://www.npmjs.com/package/ws), the module provides a helper that broadcasts to all open clients:
|
|
127
|
+
|
|
128
|
+
import { createServer } from "node:http";
|
|
129
|
+
import { WebSocketServer } from "ws";
|
|
130
|
+
import {
|
|
131
|
+
createWebSocketOutput,
|
|
132
|
+
createWsBroadcaster,
|
|
133
|
+
} from "logora-websocket";
|
|
134
|
+
|
|
135
|
+
const httpServer = createServer();
|
|
136
|
+
const webSocketServer = new WebSocketServer({ server: httpServer });
|
|
137
|
+
|
|
138
|
+
const output = createWebSocketOutput({
|
|
139
|
+
broadcaster: createWsBroadcaster(webSocketServer),
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
This helper:
|
|
143
|
+
|
|
144
|
+
- iterates over connected clients
|
|
145
|
+
- sends only to open sockets
|
|
146
|
+
- ignores failures from individual clients
|
|
147
|
+
|
|
148
|
+
* * *
|
|
149
|
+
|
|
150
|
+
## Message Format
|
|
151
|
+
|
|
152
|
+
Each writer instruction is sent as a JSON message.
|
|
153
|
+
|
|
154
|
+
### Log message
|
|
155
|
+
|
|
156
|
+
{
|
|
157
|
+
"kind": "log",
|
|
158
|
+
"entry": {
|
|
159
|
+
"timestamp": "2026-04-04T12:00:00.000Z",
|
|
160
|
+
"type": "warning",
|
|
161
|
+
"message": "Something happened",
|
|
162
|
+
"args": [123],
|
|
163
|
+
"scope": "API"
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
### Print message
|
|
168
|
+
|
|
169
|
+
{
|
|
170
|
+
"kind": "print",
|
|
171
|
+
"message": "Server listening",
|
|
172
|
+
"args": [3000]
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
### Title message
|
|
176
|
+
|
|
177
|
+
{
|
|
178
|
+
"kind": "title",
|
|
179
|
+
"title": "HTTP"
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
### Empty message
|
|
183
|
+
|
|
184
|
+
{
|
|
185
|
+
"kind": "empty",
|
|
186
|
+
"count": 2
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
### Clear message
|
|
190
|
+
|
|
191
|
+
{
|
|
192
|
+
"kind": "clear"
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
* * *
|
|
196
|
+
|
|
197
|
+
## Serialized Log Entry
|
|
198
|
+
|
|
199
|
+
A `log` instruction contains a serialized Logora entry with the following shape:
|
|
200
|
+
|
|
201
|
+
| Field | Type | Description |
|
|
202
|
+
|---|---|---|
|
|
203
|
+
| `timestamp` | `string` | ISO timestamp of the log entry |
|
|
204
|
+
| `type` | `string` | Log type name (`debug`, `info`, `success`, `warning`, `error`, `highlight`, `raw`) |
|
|
205
|
+
| `message` | `string` | Main log message |
|
|
206
|
+
| `args` | `SerializedValue[]` | Serialized argument list |
|
|
207
|
+
| `scope` | `string \| undefined` | Optional logger scope |
|
|
208
|
+
|
|
209
|
+
* * *
|
|
210
|
+
|
|
211
|
+
## Value Serialization
|
|
212
|
+
|
|
213
|
+
Arguments are serialized before being sent through the broadcaster.
|
|
214
|
+
|
|
215
|
+
### Supported conversions
|
|
216
|
+
|
|
217
|
+
- `string`, `number`, `boolean`, `null`
|
|
218
|
+
- `Date` → structured date payload
|
|
219
|
+
- `BigInt` → structured bigint payload
|
|
220
|
+
- `Error` → structured error payload
|
|
221
|
+
- `Symbol` → structured symbol payload
|
|
222
|
+
- `function` → structured function payload
|
|
223
|
+
- arrays and plain objects
|
|
224
|
+
|
|
225
|
+
### Safety behavior
|
|
226
|
+
|
|
227
|
+
The default serializer also handles:
|
|
228
|
+
|
|
229
|
+
- circular references
|
|
230
|
+
- deep objects
|
|
231
|
+
- very large arrays
|
|
232
|
+
- very large objects
|
|
233
|
+
|
|
234
|
+
Unsupported or truncated values are replaced with safe fallback representations.
|
|
235
|
+
|
|
236
|
+
* * *
|
|
237
|
+
|
|
238
|
+
## Configuration Options
|
|
239
|
+
|
|
240
|
+
| Option | Type | Default | Description |
|
|
241
|
+
|---|---|---|---|
|
|
242
|
+
| `broadcaster` | `WebSocketBroadcaster` | — | Broadcaster used to forward serialized messages |
|
|
243
|
+
| `serializer` | `WebSocketInstructionSerializer` | `DefaultWebSocketInstructionSerializer` | Serializer used to convert instructions and values |
|
|
244
|
+
| `level` | `LogLevel \| undefined` | `undefined` | Optional minimum level for this output |
|
|
245
|
+
|
|
246
|
+
* * *
|
|
247
|
+
|
|
248
|
+
## Advanced Usage
|
|
249
|
+
|
|
250
|
+
### Custom serializer
|
|
251
|
+
|
|
252
|
+
import {
|
|
253
|
+
createWebSocketOutput,
|
|
254
|
+
DefaultWebSocketInstructionSerializer,
|
|
255
|
+
} from "logora-websocket";
|
|
256
|
+
|
|
257
|
+
const output = createWebSocketOutput({
|
|
258
|
+
broadcaster,
|
|
259
|
+
serializer: new DefaultWebSocketInstructionSerializer({
|
|
260
|
+
maxDepth: 8,
|
|
261
|
+
maxArrayLength: 200,
|
|
262
|
+
maxObjectKeys: 200,
|
|
263
|
+
}),
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
### Per-output level filtering
|
|
267
|
+
|
|
268
|
+
import { LogLevel } from "logora";
|
|
269
|
+
|
|
270
|
+
const output = createWebSocketOutput({
|
|
271
|
+
broadcaster,
|
|
272
|
+
level: LogLevel.Warning,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
In this case, the WebSocket output will only receive warning and error logs according to the Logora output filtering rules.
|
|
276
|
+
|
|
277
|
+
* * *
|
|
278
|
+
|
|
279
|
+
## Notes
|
|
280
|
+
|
|
281
|
+
- This module is designed for standard WebSocket-based integrations.
|
|
282
|
+
- It does not implement Socket.IO-specific protocol behavior.
|
|
283
|
+
- It does not buffer, replay, or persist messages.
|
|
284
|
+
- It is intended as a live transport module, not a storage backend.
|
|
285
|
+
|
|
286
|
+
* * *
|
|
287
|
+
|
|
288
|
+
## License
|
|
289
|
+
|
|
290
|
+
MIT © Sébastien Bosmans
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { ILogoraOutputOptions, LogLevel, ILogoraOutput } from 'logora';
|
|
2
|
+
import { ILogoraWriter } from 'logora/module';
|
|
3
|
+
|
|
4
|
+
interface SerializedError {
|
|
5
|
+
__type: "Error";
|
|
6
|
+
name: string;
|
|
7
|
+
message: string;
|
|
8
|
+
stack?: string;
|
|
9
|
+
cause?: SerializedValue;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface SerializedDateValue {
|
|
13
|
+
__type: "Date";
|
|
14
|
+
value: string;
|
|
15
|
+
}
|
|
16
|
+
interface SerializedBigIntValue {
|
|
17
|
+
__type: "BigInt";
|
|
18
|
+
value: string;
|
|
19
|
+
}
|
|
20
|
+
interface SerializedFunctionValue {
|
|
21
|
+
__type: "Function";
|
|
22
|
+
name: string;
|
|
23
|
+
}
|
|
24
|
+
interface SerializedSymbolValue {
|
|
25
|
+
__type: "Symbol";
|
|
26
|
+
value: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type SerializedPrimitive = string | number | boolean | null;
|
|
30
|
+
interface SerializedObject {
|
|
31
|
+
[key: string]: SerializedValue;
|
|
32
|
+
}
|
|
33
|
+
type SerializedValue = SerializedPrimitive | SerializedValue[] | SerializedObject | SerializedError | SerializedDateValue | SerializedBigIntValue | SerializedFunctionValue | SerializedSymbolValue;
|
|
34
|
+
|
|
35
|
+
interface SerializedLogEntry {
|
|
36
|
+
timestamp: string;
|
|
37
|
+
type: string;
|
|
38
|
+
message: string;
|
|
39
|
+
args: SerializedValue[];
|
|
40
|
+
scope?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface WebSocketLogInstruction {
|
|
44
|
+
kind: "log";
|
|
45
|
+
entry: SerializedLogEntry;
|
|
46
|
+
}
|
|
47
|
+
interface WebSocketPrintInstruction {
|
|
48
|
+
kind: "print";
|
|
49
|
+
message: string;
|
|
50
|
+
args: SerializedValue[];
|
|
51
|
+
}
|
|
52
|
+
interface WebSocketTitleInstruction {
|
|
53
|
+
kind: "title";
|
|
54
|
+
title: string;
|
|
55
|
+
}
|
|
56
|
+
interface WebSocketEmptyInstruction {
|
|
57
|
+
kind: "empty";
|
|
58
|
+
count: number;
|
|
59
|
+
}
|
|
60
|
+
interface WebSocketClearInstruction {
|
|
61
|
+
kind: "clear";
|
|
62
|
+
}
|
|
63
|
+
type WebSocketInstruction = WebSocketLogInstruction | WebSocketPrintInstruction | WebSocketTitleInstruction | WebSocketEmptyInstruction | WebSocketClearInstruction;
|
|
64
|
+
|
|
65
|
+
interface WebSocketInstructionSerializer {
|
|
66
|
+
serialize(instruction: WebSocketInstruction): string;
|
|
67
|
+
serializeValue(value: unknown): SerializedValue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface WebSocketBroadcaster {
|
|
71
|
+
broadcast(message: string): void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
declare class WebSocketOutputOptions implements ILogoraOutputOptions {
|
|
75
|
+
level?: LogLevel;
|
|
76
|
+
broadcaster: WebSocketBroadcaster;
|
|
77
|
+
serializer: WebSocketInstructionSerializer;
|
|
78
|
+
constructor(overrides?: Partial<WebSocketOutputOptions>);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
declare class WebSocketOutput implements ILogoraOutput {
|
|
82
|
+
name: string;
|
|
83
|
+
options: WebSocketOutputOptions;
|
|
84
|
+
writer: ILogoraWriter;
|
|
85
|
+
constructor(config?: Partial<WebSocketOutputOptions>);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
type WsLikeClient = {
|
|
89
|
+
readyState: number;
|
|
90
|
+
send(data: string): void;
|
|
91
|
+
};
|
|
92
|
+
type WsLikeServer = {
|
|
93
|
+
clients: Iterable<WsLikeClient>;
|
|
94
|
+
};
|
|
95
|
+
declare function createWsBroadcaster(server: WsLikeServer): WebSocketBroadcaster;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Creates a new WebSocket output transport for Logora.
|
|
99
|
+
*
|
|
100
|
+
* This function initializes a WebSocketOutput instance, which can be
|
|
101
|
+
* attached to a Logora logger to forward writer instructions as
|
|
102
|
+
* structured messages through a broadcaster abstraction.
|
|
103
|
+
*
|
|
104
|
+
* @param config Optional partial configuration to customize broadcaster
|
|
105
|
+
* and serialization behavior.
|
|
106
|
+
* @returns A fully initialized WebSocketOutput instance ready to use with Logora.
|
|
107
|
+
*/
|
|
108
|
+
declare function createWebSocketOutput(config?: Partial<WebSocketOutputOptions>): WebSocketOutput;
|
|
109
|
+
|
|
110
|
+
export { type SerializedBigIntValue, type SerializedDateValue, type SerializedError, type SerializedFunctionValue, type SerializedLogEntry, type SerializedObject, type SerializedPrimitive, type SerializedSymbolValue, type SerializedValue, type WebSocketBroadcaster, type WebSocketClearInstruction, type WebSocketEmptyInstruction, type WebSocketInstruction, type WebSocketLogInstruction, WebSocketOutputOptions, type WebSocketPrintInstruction, type WebSocketTitleInstruction, createWebSocketOutput, createWsBroadcaster };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { ILogoraOutputOptions, LogLevel, ILogoraOutput } from 'logora';
|
|
2
|
+
import { ILogoraWriter } from 'logora/module';
|
|
3
|
+
|
|
4
|
+
interface SerializedError {
|
|
5
|
+
__type: "Error";
|
|
6
|
+
name: string;
|
|
7
|
+
message: string;
|
|
8
|
+
stack?: string;
|
|
9
|
+
cause?: SerializedValue;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface SerializedDateValue {
|
|
13
|
+
__type: "Date";
|
|
14
|
+
value: string;
|
|
15
|
+
}
|
|
16
|
+
interface SerializedBigIntValue {
|
|
17
|
+
__type: "BigInt";
|
|
18
|
+
value: string;
|
|
19
|
+
}
|
|
20
|
+
interface SerializedFunctionValue {
|
|
21
|
+
__type: "Function";
|
|
22
|
+
name: string;
|
|
23
|
+
}
|
|
24
|
+
interface SerializedSymbolValue {
|
|
25
|
+
__type: "Symbol";
|
|
26
|
+
value: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type SerializedPrimitive = string | number | boolean | null;
|
|
30
|
+
interface SerializedObject {
|
|
31
|
+
[key: string]: SerializedValue;
|
|
32
|
+
}
|
|
33
|
+
type SerializedValue = SerializedPrimitive | SerializedValue[] | SerializedObject | SerializedError | SerializedDateValue | SerializedBigIntValue | SerializedFunctionValue | SerializedSymbolValue;
|
|
34
|
+
|
|
35
|
+
interface SerializedLogEntry {
|
|
36
|
+
timestamp: string;
|
|
37
|
+
type: string;
|
|
38
|
+
message: string;
|
|
39
|
+
args: SerializedValue[];
|
|
40
|
+
scope?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface WebSocketLogInstruction {
|
|
44
|
+
kind: "log";
|
|
45
|
+
entry: SerializedLogEntry;
|
|
46
|
+
}
|
|
47
|
+
interface WebSocketPrintInstruction {
|
|
48
|
+
kind: "print";
|
|
49
|
+
message: string;
|
|
50
|
+
args: SerializedValue[];
|
|
51
|
+
}
|
|
52
|
+
interface WebSocketTitleInstruction {
|
|
53
|
+
kind: "title";
|
|
54
|
+
title: string;
|
|
55
|
+
}
|
|
56
|
+
interface WebSocketEmptyInstruction {
|
|
57
|
+
kind: "empty";
|
|
58
|
+
count: number;
|
|
59
|
+
}
|
|
60
|
+
interface WebSocketClearInstruction {
|
|
61
|
+
kind: "clear";
|
|
62
|
+
}
|
|
63
|
+
type WebSocketInstruction = WebSocketLogInstruction | WebSocketPrintInstruction | WebSocketTitleInstruction | WebSocketEmptyInstruction | WebSocketClearInstruction;
|
|
64
|
+
|
|
65
|
+
interface WebSocketInstructionSerializer {
|
|
66
|
+
serialize(instruction: WebSocketInstruction): string;
|
|
67
|
+
serializeValue(value: unknown): SerializedValue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface WebSocketBroadcaster {
|
|
71
|
+
broadcast(message: string): void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
declare class WebSocketOutputOptions implements ILogoraOutputOptions {
|
|
75
|
+
level?: LogLevel;
|
|
76
|
+
broadcaster: WebSocketBroadcaster;
|
|
77
|
+
serializer: WebSocketInstructionSerializer;
|
|
78
|
+
constructor(overrides?: Partial<WebSocketOutputOptions>);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
declare class WebSocketOutput implements ILogoraOutput {
|
|
82
|
+
name: string;
|
|
83
|
+
options: WebSocketOutputOptions;
|
|
84
|
+
writer: ILogoraWriter;
|
|
85
|
+
constructor(config?: Partial<WebSocketOutputOptions>);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
type WsLikeClient = {
|
|
89
|
+
readyState: number;
|
|
90
|
+
send(data: string): void;
|
|
91
|
+
};
|
|
92
|
+
type WsLikeServer = {
|
|
93
|
+
clients: Iterable<WsLikeClient>;
|
|
94
|
+
};
|
|
95
|
+
declare function createWsBroadcaster(server: WsLikeServer): WebSocketBroadcaster;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Creates a new WebSocket output transport for Logora.
|
|
99
|
+
*
|
|
100
|
+
* This function initializes a WebSocketOutput instance, which can be
|
|
101
|
+
* attached to a Logora logger to forward writer instructions as
|
|
102
|
+
* structured messages through a broadcaster abstraction.
|
|
103
|
+
*
|
|
104
|
+
* @param config Optional partial configuration to customize broadcaster
|
|
105
|
+
* and serialization behavior.
|
|
106
|
+
* @returns A fully initialized WebSocketOutput instance ready to use with Logora.
|
|
107
|
+
*/
|
|
108
|
+
declare function createWebSocketOutput(config?: Partial<WebSocketOutputOptions>): WebSocketOutput;
|
|
109
|
+
|
|
110
|
+
export { type SerializedBigIntValue, type SerializedDateValue, type SerializedError, type SerializedFunctionValue, type SerializedLogEntry, type SerializedObject, type SerializedPrimitive, type SerializedSymbolValue, type SerializedValue, type WebSocketBroadcaster, type WebSocketClearInstruction, type WebSocketEmptyInstruction, type WebSocketInstruction, type WebSocketLogInstruction, WebSocketOutputOptions, type WebSocketPrintInstruction, type WebSocketTitleInstruction, createWebSocketOutput, createWsBroadcaster };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
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
|
+
WebSocketOutputOptions: () => WebSocketOutputOptions,
|
|
24
|
+
createWebSocketOutput: () => createWebSocketOutput,
|
|
25
|
+
createWsBroadcaster: () => createWsBroadcaster
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/core/instruction-serializer.ts
|
|
30
|
+
var DefaultWebSocketInstructionSerializer = class {
|
|
31
|
+
constructor(config) {
|
|
32
|
+
var _a, _b, _c;
|
|
33
|
+
this._maxDepth = (_a = config == null ? void 0 : config.maxDepth) != null ? _a : 6;
|
|
34
|
+
this._maxArrayLength = (_b = config == null ? void 0 : config.maxArrayLength) != null ? _b : 100;
|
|
35
|
+
this._maxObjectKeys = (_c = config == null ? void 0 : config.maxObjectKeys) != null ? _c : 100;
|
|
36
|
+
}
|
|
37
|
+
serialize(instruction) {
|
|
38
|
+
return JSON.stringify(instruction);
|
|
39
|
+
}
|
|
40
|
+
serializeValue(value) {
|
|
41
|
+
return this._serializeInternal(value, {
|
|
42
|
+
visitedObjects: /* @__PURE__ */ new WeakSet(),
|
|
43
|
+
depth: 0
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
_serializeInternal(value, context) {
|
|
47
|
+
if (value === null || value === void 0) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if (typeof value === "string") {
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
if (typeof value === "number") {
|
|
54
|
+
return Number.isFinite(value) ? value : String(value);
|
|
55
|
+
}
|
|
56
|
+
if (typeof value === "boolean") {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
if (typeof value === "symbol") {
|
|
60
|
+
return {
|
|
61
|
+
__type: "Symbol",
|
|
62
|
+
value: value.toString()
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (typeof value === "function") {
|
|
66
|
+
return {
|
|
67
|
+
__type: "Function",
|
|
68
|
+
name: value.name || "anonymous"
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (value instanceof Date) {
|
|
72
|
+
return {
|
|
73
|
+
__type: "Date",
|
|
74
|
+
value: value.toISOString()
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (value instanceof Error) {
|
|
78
|
+
return this._serializeError(value, context);
|
|
79
|
+
}
|
|
80
|
+
if (context.depth >= this._maxDepth) {
|
|
81
|
+
return "[MaxDepthExceeded]";
|
|
82
|
+
}
|
|
83
|
+
if (Array.isArray(value)) {
|
|
84
|
+
return this._serializeArray(value, context);
|
|
85
|
+
}
|
|
86
|
+
if (typeof value === "object") {
|
|
87
|
+
return this._serializeObject(value, context);
|
|
88
|
+
}
|
|
89
|
+
return "[UnsupportedValue]";
|
|
90
|
+
}
|
|
91
|
+
_serializeError(value, context) {
|
|
92
|
+
const serializedError = {
|
|
93
|
+
__type: "Error",
|
|
94
|
+
name: value.name,
|
|
95
|
+
message: value.message
|
|
96
|
+
};
|
|
97
|
+
if (value.stack) {
|
|
98
|
+
serializedError.stack = value.stack;
|
|
99
|
+
}
|
|
100
|
+
const errorWithCause = value;
|
|
101
|
+
if (errorWithCause.cause !== void 0) {
|
|
102
|
+
serializedError.cause = this._serializeInternal(errorWithCause.cause, {
|
|
103
|
+
...context,
|
|
104
|
+
depth: context.depth + 1
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return serializedError;
|
|
108
|
+
}
|
|
109
|
+
_serializeArray(value, context) {
|
|
110
|
+
return value.slice(0, this._maxArrayLength).map(
|
|
111
|
+
(item) => this._serializeInternal(item, {
|
|
112
|
+
...context,
|
|
113
|
+
depth: context.depth + 1
|
|
114
|
+
})
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
_serializeObject(value, context) {
|
|
118
|
+
if (context.visitedObjects.has(value)) {
|
|
119
|
+
return "[Circular]";
|
|
120
|
+
}
|
|
121
|
+
context.visitedObjects.add(value);
|
|
122
|
+
const serializedObject = {};
|
|
123
|
+
const entries = Object.entries(value).slice(
|
|
124
|
+
0,
|
|
125
|
+
this._maxObjectKeys
|
|
126
|
+
);
|
|
127
|
+
for (const [key, entryValue] of entries) {
|
|
128
|
+
serializedObject[key] = this._serializeInternal(entryValue, {
|
|
129
|
+
...context,
|
|
130
|
+
depth: context.depth + 1
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return serializedObject;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// src/config/websocket-output-options.ts
|
|
138
|
+
var WebSocketOutputOptions = class {
|
|
139
|
+
constructor(overrides) {
|
|
140
|
+
this.serializer = new DefaultWebSocketInstructionSerializer();
|
|
141
|
+
Object.assign(this, overrides);
|
|
142
|
+
if (!this.broadcaster) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
"WebSocketOutputOptions requires a broadcaster instance."
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// src/core/instruction-factory.ts
|
|
151
|
+
var import_logora = require("logora");
|
|
152
|
+
var WebSocketInstructionFactory = class {
|
|
153
|
+
constructor(_serializer) {
|
|
154
|
+
this._serializer = _serializer;
|
|
155
|
+
}
|
|
156
|
+
createLog(entry) {
|
|
157
|
+
return {
|
|
158
|
+
kind: "log",
|
|
159
|
+
entry: this._serializeLogEntry(entry)
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
createPrint(message, args) {
|
|
163
|
+
return {
|
|
164
|
+
kind: "print",
|
|
165
|
+
message,
|
|
166
|
+
args: args.map(
|
|
167
|
+
(argument) => this._serializer.serializeValue(argument)
|
|
168
|
+
)
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
createTitle(title) {
|
|
172
|
+
return {
|
|
173
|
+
kind: "title",
|
|
174
|
+
title
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
createEmpty(count) {
|
|
178
|
+
return {
|
|
179
|
+
kind: "empty",
|
|
180
|
+
count
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
createClear() {
|
|
184
|
+
return {
|
|
185
|
+
kind: "clear"
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
_serializeLogEntry(entry) {
|
|
189
|
+
return {
|
|
190
|
+
timestamp: entry.timestamp.toISOString(),
|
|
191
|
+
type: this._serializeLogType(entry.type),
|
|
192
|
+
message: entry.message,
|
|
193
|
+
args: entry.args.map(
|
|
194
|
+
(argument) => this._serializer.serializeValue(argument)
|
|
195
|
+
),
|
|
196
|
+
scope: entry.scope
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
_serializeLogType(type) {
|
|
200
|
+
switch (type) {
|
|
201
|
+
case import_logora.LogType.Debug:
|
|
202
|
+
return "debug";
|
|
203
|
+
case import_logora.LogType.Info:
|
|
204
|
+
return "info";
|
|
205
|
+
case import_logora.LogType.Success:
|
|
206
|
+
return "success";
|
|
207
|
+
case import_logora.LogType.Warning:
|
|
208
|
+
return "warning";
|
|
209
|
+
case import_logora.LogType.Error:
|
|
210
|
+
return "error";
|
|
211
|
+
case import_logora.LogType.Highlight:
|
|
212
|
+
return "highlight";
|
|
213
|
+
case import_logora.LogType.Raw:
|
|
214
|
+
return "raw";
|
|
215
|
+
default:
|
|
216
|
+
return String(type);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// src/core/writer.ts
|
|
222
|
+
var WebSocketWriter = class {
|
|
223
|
+
constructor(_options) {
|
|
224
|
+
this._options = _options;
|
|
225
|
+
this._instructionFactory = new WebSocketInstructionFactory(
|
|
226
|
+
this._options.serializer
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
log(entry) {
|
|
230
|
+
this._dispatch(this._instructionFactory.createLog(entry));
|
|
231
|
+
}
|
|
232
|
+
title(title) {
|
|
233
|
+
this._dispatch(this._instructionFactory.createTitle(title));
|
|
234
|
+
}
|
|
235
|
+
empty(count = 1) {
|
|
236
|
+
this._dispatch(this._instructionFactory.createEmpty(Math.max(0, count)));
|
|
237
|
+
}
|
|
238
|
+
clear() {
|
|
239
|
+
this._dispatch(this._instructionFactory.createClear());
|
|
240
|
+
}
|
|
241
|
+
print(message, ...args) {
|
|
242
|
+
this._dispatch(this._instructionFactory.createPrint(message, args));
|
|
243
|
+
}
|
|
244
|
+
_dispatch(instruction) {
|
|
245
|
+
const payload = this._options.serializer.serialize(instruction);
|
|
246
|
+
this._options.broadcaster.broadcast(payload);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// src/core/output.ts
|
|
251
|
+
var WebSocketOutput = class {
|
|
252
|
+
constructor(config) {
|
|
253
|
+
this.name = "websocket";
|
|
254
|
+
this.options = new WebSocketOutputOptions(config);
|
|
255
|
+
this.writer = new WebSocketWriter(this.options);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// src/core/ws-broadcaster.ts
|
|
260
|
+
var OPEN_READY_STATE = 1;
|
|
261
|
+
function createWsBroadcaster(server) {
|
|
262
|
+
return {
|
|
263
|
+
broadcast(message) {
|
|
264
|
+
for (const client of server.clients) {
|
|
265
|
+
if (client.readyState !== OPEN_READY_STATE) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
client.send(message);
|
|
270
|
+
} catch {
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/index.ts
|
|
278
|
+
function createWebSocketOutput(config) {
|
|
279
|
+
return new WebSocketOutput(config);
|
|
280
|
+
}
|
|
281
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
282
|
+
0 && (module.exports = {
|
|
283
|
+
WebSocketOutputOptions,
|
|
284
|
+
createWebSocketOutput,
|
|
285
|
+
createWsBroadcaster
|
|
286
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// src/core/instruction-serializer.ts
|
|
2
|
+
var DefaultWebSocketInstructionSerializer = class {
|
|
3
|
+
constructor(config) {
|
|
4
|
+
var _a, _b, _c;
|
|
5
|
+
this._maxDepth = (_a = config == null ? void 0 : config.maxDepth) != null ? _a : 6;
|
|
6
|
+
this._maxArrayLength = (_b = config == null ? void 0 : config.maxArrayLength) != null ? _b : 100;
|
|
7
|
+
this._maxObjectKeys = (_c = config == null ? void 0 : config.maxObjectKeys) != null ? _c : 100;
|
|
8
|
+
}
|
|
9
|
+
serialize(instruction) {
|
|
10
|
+
return JSON.stringify(instruction);
|
|
11
|
+
}
|
|
12
|
+
serializeValue(value) {
|
|
13
|
+
return this._serializeInternal(value, {
|
|
14
|
+
visitedObjects: /* @__PURE__ */ new WeakSet(),
|
|
15
|
+
depth: 0
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
_serializeInternal(value, context) {
|
|
19
|
+
if (value === null || value === void 0) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
if (typeof value === "string") {
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
if (typeof value === "number") {
|
|
26
|
+
return Number.isFinite(value) ? value : String(value);
|
|
27
|
+
}
|
|
28
|
+
if (typeof value === "boolean") {
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
if (typeof value === "symbol") {
|
|
32
|
+
return {
|
|
33
|
+
__type: "Symbol",
|
|
34
|
+
value: value.toString()
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (typeof value === "function") {
|
|
38
|
+
return {
|
|
39
|
+
__type: "Function",
|
|
40
|
+
name: value.name || "anonymous"
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (value instanceof Date) {
|
|
44
|
+
return {
|
|
45
|
+
__type: "Date",
|
|
46
|
+
value: value.toISOString()
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (value instanceof Error) {
|
|
50
|
+
return this._serializeError(value, context);
|
|
51
|
+
}
|
|
52
|
+
if (context.depth >= this._maxDepth) {
|
|
53
|
+
return "[MaxDepthExceeded]";
|
|
54
|
+
}
|
|
55
|
+
if (Array.isArray(value)) {
|
|
56
|
+
return this._serializeArray(value, context);
|
|
57
|
+
}
|
|
58
|
+
if (typeof value === "object") {
|
|
59
|
+
return this._serializeObject(value, context);
|
|
60
|
+
}
|
|
61
|
+
return "[UnsupportedValue]";
|
|
62
|
+
}
|
|
63
|
+
_serializeError(value, context) {
|
|
64
|
+
const serializedError = {
|
|
65
|
+
__type: "Error",
|
|
66
|
+
name: value.name,
|
|
67
|
+
message: value.message
|
|
68
|
+
};
|
|
69
|
+
if (value.stack) {
|
|
70
|
+
serializedError.stack = value.stack;
|
|
71
|
+
}
|
|
72
|
+
const errorWithCause = value;
|
|
73
|
+
if (errorWithCause.cause !== void 0) {
|
|
74
|
+
serializedError.cause = this._serializeInternal(errorWithCause.cause, {
|
|
75
|
+
...context,
|
|
76
|
+
depth: context.depth + 1
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return serializedError;
|
|
80
|
+
}
|
|
81
|
+
_serializeArray(value, context) {
|
|
82
|
+
return value.slice(0, this._maxArrayLength).map(
|
|
83
|
+
(item) => this._serializeInternal(item, {
|
|
84
|
+
...context,
|
|
85
|
+
depth: context.depth + 1
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
_serializeObject(value, context) {
|
|
90
|
+
if (context.visitedObjects.has(value)) {
|
|
91
|
+
return "[Circular]";
|
|
92
|
+
}
|
|
93
|
+
context.visitedObjects.add(value);
|
|
94
|
+
const serializedObject = {};
|
|
95
|
+
const entries = Object.entries(value).slice(
|
|
96
|
+
0,
|
|
97
|
+
this._maxObjectKeys
|
|
98
|
+
);
|
|
99
|
+
for (const [key, entryValue] of entries) {
|
|
100
|
+
serializedObject[key] = this._serializeInternal(entryValue, {
|
|
101
|
+
...context,
|
|
102
|
+
depth: context.depth + 1
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return serializedObject;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// src/config/websocket-output-options.ts
|
|
110
|
+
var WebSocketOutputOptions = class {
|
|
111
|
+
constructor(overrides) {
|
|
112
|
+
this.serializer = new DefaultWebSocketInstructionSerializer();
|
|
113
|
+
Object.assign(this, overrides);
|
|
114
|
+
if (!this.broadcaster) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
"WebSocketOutputOptions requires a broadcaster instance."
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// src/core/instruction-factory.ts
|
|
123
|
+
import { LogType } from "logora";
|
|
124
|
+
var WebSocketInstructionFactory = class {
|
|
125
|
+
constructor(_serializer) {
|
|
126
|
+
this._serializer = _serializer;
|
|
127
|
+
}
|
|
128
|
+
createLog(entry) {
|
|
129
|
+
return {
|
|
130
|
+
kind: "log",
|
|
131
|
+
entry: this._serializeLogEntry(entry)
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
createPrint(message, args) {
|
|
135
|
+
return {
|
|
136
|
+
kind: "print",
|
|
137
|
+
message,
|
|
138
|
+
args: args.map(
|
|
139
|
+
(argument) => this._serializer.serializeValue(argument)
|
|
140
|
+
)
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
createTitle(title) {
|
|
144
|
+
return {
|
|
145
|
+
kind: "title",
|
|
146
|
+
title
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
createEmpty(count) {
|
|
150
|
+
return {
|
|
151
|
+
kind: "empty",
|
|
152
|
+
count
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
createClear() {
|
|
156
|
+
return {
|
|
157
|
+
kind: "clear"
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
_serializeLogEntry(entry) {
|
|
161
|
+
return {
|
|
162
|
+
timestamp: entry.timestamp.toISOString(),
|
|
163
|
+
type: this._serializeLogType(entry.type),
|
|
164
|
+
message: entry.message,
|
|
165
|
+
args: entry.args.map(
|
|
166
|
+
(argument) => this._serializer.serializeValue(argument)
|
|
167
|
+
),
|
|
168
|
+
scope: entry.scope
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
_serializeLogType(type) {
|
|
172
|
+
switch (type) {
|
|
173
|
+
case LogType.Debug:
|
|
174
|
+
return "debug";
|
|
175
|
+
case LogType.Info:
|
|
176
|
+
return "info";
|
|
177
|
+
case LogType.Success:
|
|
178
|
+
return "success";
|
|
179
|
+
case LogType.Warning:
|
|
180
|
+
return "warning";
|
|
181
|
+
case LogType.Error:
|
|
182
|
+
return "error";
|
|
183
|
+
case LogType.Highlight:
|
|
184
|
+
return "highlight";
|
|
185
|
+
case LogType.Raw:
|
|
186
|
+
return "raw";
|
|
187
|
+
default:
|
|
188
|
+
return String(type);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// src/core/writer.ts
|
|
194
|
+
var WebSocketWriter = class {
|
|
195
|
+
constructor(_options) {
|
|
196
|
+
this._options = _options;
|
|
197
|
+
this._instructionFactory = new WebSocketInstructionFactory(
|
|
198
|
+
this._options.serializer
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
log(entry) {
|
|
202
|
+
this._dispatch(this._instructionFactory.createLog(entry));
|
|
203
|
+
}
|
|
204
|
+
title(title) {
|
|
205
|
+
this._dispatch(this._instructionFactory.createTitle(title));
|
|
206
|
+
}
|
|
207
|
+
empty(count = 1) {
|
|
208
|
+
this._dispatch(this._instructionFactory.createEmpty(Math.max(0, count)));
|
|
209
|
+
}
|
|
210
|
+
clear() {
|
|
211
|
+
this._dispatch(this._instructionFactory.createClear());
|
|
212
|
+
}
|
|
213
|
+
print(message, ...args) {
|
|
214
|
+
this._dispatch(this._instructionFactory.createPrint(message, args));
|
|
215
|
+
}
|
|
216
|
+
_dispatch(instruction) {
|
|
217
|
+
const payload = this._options.serializer.serialize(instruction);
|
|
218
|
+
this._options.broadcaster.broadcast(payload);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// src/core/output.ts
|
|
223
|
+
var WebSocketOutput = class {
|
|
224
|
+
constructor(config) {
|
|
225
|
+
this.name = "websocket";
|
|
226
|
+
this.options = new WebSocketOutputOptions(config);
|
|
227
|
+
this.writer = new WebSocketWriter(this.options);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// src/core/ws-broadcaster.ts
|
|
232
|
+
var OPEN_READY_STATE = 1;
|
|
233
|
+
function createWsBroadcaster(server) {
|
|
234
|
+
return {
|
|
235
|
+
broadcast(message) {
|
|
236
|
+
for (const client of server.clients) {
|
|
237
|
+
if (client.readyState !== OPEN_READY_STATE) {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
client.send(message);
|
|
242
|
+
} catch {
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// src/index.ts
|
|
250
|
+
function createWebSocketOutput(config) {
|
|
251
|
+
return new WebSocketOutput(config);
|
|
252
|
+
}
|
|
253
|
+
export {
|
|
254
|
+
WebSocketOutputOptions,
|
|
255
|
+
createWebSocketOutput,
|
|
256
|
+
createWsBroadcaster
|
|
257
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "logora-websocket",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "websocket plugin for Logora – enables real-time communication.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"require": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"lint": "eslint src tests --ext .ts",
|
|
18
|
+
"lint:fix": "eslint src tests --ext .ts --fix",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"test:dev": "vitest",
|
|
21
|
+
"test:coverage": "vitest run --coverage",
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"build:dev": "tsup --watch",
|
|
24
|
+
"build:prod": "npm run clean && npm run lint && npm run build",
|
|
25
|
+
"clean": "rimraf dist",
|
|
26
|
+
"check": "npm run lint && npm run test && npm run build",
|
|
27
|
+
"prepare": "npm run build",
|
|
28
|
+
"prepublishOnly": "npm run build:prod"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"logger",
|
|
32
|
+
"logora",
|
|
33
|
+
"typescript",
|
|
34
|
+
"logging",
|
|
35
|
+
"websocket"
|
|
36
|
+
],
|
|
37
|
+
"author": "Sébastien Bosmans <https://github.com/boseba>",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/boseba/logora-websocket.git"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/boseba/logora-websocket/issues"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/boseba/logora-websocket#readme",
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=16"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@eslint/js": "^9.25.1",
|
|
52
|
+
"@vitest/coverage-v8": "^3.1.2",
|
|
53
|
+
"eslint": "^9.25.1",
|
|
54
|
+
"rimraf": "^6.0.1",
|
|
55
|
+
"tsup": "^8.4.0",
|
|
56
|
+
"typescript": "^5.8.3",
|
|
57
|
+
"typescript-eslint": "^8.31.0",
|
|
58
|
+
"vitest": "^3.1.2"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"dayjs": "^1.11.13",
|
|
62
|
+
"logora": "^2.0.5"
|
|
63
|
+
}
|
|
64
|
+
}
|