@logtape/logtape 0.1.0-dev.9 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +341 -0
- package/esm/config.js +35 -1
- package/esm/filesink.node.js +32 -0
- package/esm/filesink.web.js +12 -0
- package/esm/formatter.js +10 -6
- package/esm/mod.js +5 -3
- package/esm/sink.js +31 -10
- package/package.json +6 -1
- package/script/config.js +60 -2
- package/script/filesink.node.js +62 -0
- package/script/filesink.web.js +15 -0
- package/script/formatter.js +10 -6
- package/script/mod.js +7 -1
- package/script/sink.js +33 -11
- package/types/config.d.ts +7 -0
- package/types/config.d.ts.map +1 -1
- package/types/deps/deno.land/x/which_runtime@0.2.0/mod.d.ts.map +1 -0
- package/types/filesink.node.d.ts +18 -0
- package/types/filesink.node.d.ts.map +1 -0
- package/types/filesink.test.d.ts.map +1 -0
- package/types/filesink.web.d.ts +6 -0
- package/types/filesink.web.d.ts.map +1 -0
- package/types/formatter.d.ts +1 -1
- package/types/formatter.d.ts.map +1 -1
- package/types/mod.d.ts +4 -3
- package/types/mod.d.ts.map +1 -1
- package/types/sink.d.ts +74 -11
- package/types/sink.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -3,16 +3,357 @@
|
|
|
3
3
|
LogTape
|
|
4
4
|
=======
|
|
5
5
|
|
|
6
|
+
[![JSR][JSR badge]][JSR]
|
|
7
|
+
[![npm][npm badge]][npm]
|
|
6
8
|
[![GitHub Actions][GitHub Actions badge]][GitHub Actions]
|
|
7
9
|
[![Codecov][Codecov badge]][Codecov]
|
|
8
10
|
|
|
11
|
+
> [!NOTE]
|
|
12
|
+
> LogTape is still in the early stage of development. The API is not stable
|
|
13
|
+
> yet. Please be careful when using it in production.
|
|
14
|
+
|
|
9
15
|
LogTape is a simple logging library for Deno/Node.js/Bun/browsers. It is
|
|
10
16
|
designed to be used for both applications and libraries.
|
|
11
17
|
|
|
12
18
|
Currently, LogTape provides only few sinks, but you can easily add your own
|
|
13
19
|
sinks.
|
|
14
20
|
|
|
21
|
+

|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
[JSR]: https://jsr.io/@logtape/logtape
|
|
25
|
+
[JSR badge]: https://jsr.io/badges/@logtape/logtape
|
|
26
|
+
[npm]: https://www.npmjs.com/package/@logtape/logtape
|
|
27
|
+
[npm badge]: https://img.shields.io/npm/v/@logtape/logtape?logo=npm
|
|
15
28
|
[GitHub Actions]: https://github.com/dahlia/logtape/actions/workflows/main.yaml
|
|
16
29
|
[GitHub Actions badge]: https://github.com/dahlia/logtape/actions/workflows/main.yaml/badge.svg
|
|
17
30
|
[Codecov]: https://codecov.io/gh/dahlia/logtape
|
|
18
31
|
[Codecov badge]: https://codecov.io/gh/dahlia/logtape/graph/badge.svg?token=yOejfcuX7r
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
Installation
|
|
35
|
+
------------
|
|
36
|
+
|
|
37
|
+
### Deno
|
|
38
|
+
|
|
39
|
+
~~~~ sh
|
|
40
|
+
deno add @logtape/logtape
|
|
41
|
+
~~~~
|
|
42
|
+
|
|
43
|
+
### Node.js
|
|
44
|
+
|
|
45
|
+
~~~~ sh
|
|
46
|
+
npm add @logtape/logtape
|
|
47
|
+
~~~~
|
|
48
|
+
|
|
49
|
+
### Bun
|
|
50
|
+
|
|
51
|
+
~~~~ sh
|
|
52
|
+
bun add @logtape/logtape
|
|
53
|
+
~~~~
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
Quick start
|
|
57
|
+
-----------
|
|
58
|
+
|
|
59
|
+
Set up LogTape in the entry point of your application (if you are composing
|
|
60
|
+
a library, you should not set up LogTape in the library itself; it is up to
|
|
61
|
+
the application to set up LogTape):
|
|
62
|
+
|
|
63
|
+
~~~~ typescript
|
|
64
|
+
import { configure, getConsoleSink } from "@logtape/logtape";
|
|
65
|
+
|
|
66
|
+
configure({
|
|
67
|
+
sinks: { console: getConsoleSink() },
|
|
68
|
+
filters: {},
|
|
69
|
+
loggers: [
|
|
70
|
+
{ category: "my-app", level: "debug", sinks: ["console"] }
|
|
71
|
+
]
|
|
72
|
+
});
|
|
73
|
+
~~~~
|
|
74
|
+
|
|
75
|
+
And then you can use LogTape in your application or library:
|
|
76
|
+
|
|
77
|
+
~~~~ typescript
|
|
78
|
+
import { getLogger } from "@logtape/logtape";
|
|
79
|
+
|
|
80
|
+
const logger = getLogger(["my-app", "my-module"]);
|
|
81
|
+
|
|
82
|
+
export function myFunc(value: number): void {
|
|
83
|
+
logger.debug `Hello, ${value}!`;
|
|
84
|
+
}
|
|
85
|
+
~~~~
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
How to log
|
|
89
|
+
----------
|
|
90
|
+
|
|
91
|
+
There are total 5 log levels: `debug`, `info`, `warning`, `error`, `fatal` (in
|
|
92
|
+
the order of verbosity). You can log messages with the following syntax:
|
|
93
|
+
|
|
94
|
+
~~~~ typescript
|
|
95
|
+
logger.debug `This is a debug message with ${value}.`;
|
|
96
|
+
logger.info `This is an info message with ${value}.`;
|
|
97
|
+
logger.warn `This is a warning message with ${value}.`;
|
|
98
|
+
logger.error `This is an error message with ${value}.`;
|
|
99
|
+
logger.fatal `This is a fatal message with ${value}.`;
|
|
100
|
+
~~~~
|
|
101
|
+
|
|
102
|
+
You can also log messages with a function call:
|
|
103
|
+
|
|
104
|
+
~~~~ typescript
|
|
105
|
+
logger.debug("This is a debug message with {value}.", { value });
|
|
106
|
+
logger.info("This is an info message with {value}.", { value });
|
|
107
|
+
logger.warn("This is a warning message with {value}.", { value });
|
|
108
|
+
logger.error("This is an error message with {value}.", { value });
|
|
109
|
+
logger.fatal("This is a fatal message with {value}.", { value });
|
|
110
|
+
~~~~
|
|
111
|
+
|
|
112
|
+
Sometimes, values to be logged are expensive to compute. In such cases, you
|
|
113
|
+
can use a function to defer the computation so that it is only computed when
|
|
114
|
+
the log message is actually logged:
|
|
115
|
+
|
|
116
|
+
~~~~ typescript
|
|
117
|
+
logger.debug(l => l`This is a debug message with ${computeValue()}.`);
|
|
118
|
+
logger.debug("Or you can use a function call: {value}.", () => {
|
|
119
|
+
return { value: computeValue() };
|
|
120
|
+
});
|
|
121
|
+
~~~~
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
Categories
|
|
125
|
+
----------
|
|
126
|
+
|
|
127
|
+
LogTape uses a hierarchical category system to manage loggers. A category is
|
|
128
|
+
a list of strings. For example, `["my-app", "my-module"]` is a category.
|
|
129
|
+
|
|
130
|
+
When you log a message, it is dispatched to all loggers whose categories are
|
|
131
|
+
prefixes of the category of the logger. For example, if you log a message
|
|
132
|
+
with the category `["my-app", "my-module", "my-submodule"]`, it is dispatched
|
|
133
|
+
to loggers whose categories are `["my-app", "my-module"]` and `["my-app"]`.
|
|
134
|
+
|
|
135
|
+
This behavior allows you to control the verbosity of log messages by setting
|
|
136
|
+
the log level of loggers at different levels of the category hierarchy.
|
|
137
|
+
|
|
138
|
+
Here's an example of setting log levels for different categories:
|
|
139
|
+
|
|
140
|
+
~~~~ typescript
|
|
141
|
+
import { configure, getConsoleSink } from "@logtape/logtape";
|
|
142
|
+
|
|
143
|
+
configure({
|
|
144
|
+
sinks: {
|
|
145
|
+
console: getConsoleSink(),
|
|
146
|
+
},
|
|
147
|
+
filters: {},
|
|
148
|
+
loggers: [
|
|
149
|
+
{ category: ["my-app"], level: "info", sinks: ["console"] },
|
|
150
|
+
{ category: ["my-app", "my-module"], level: "debug", sinks: ["console"] },
|
|
151
|
+
],
|
|
152
|
+
})
|
|
153
|
+
~~~~
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
Sinks
|
|
157
|
+
-----
|
|
158
|
+
|
|
159
|
+
A sink is a destination of log messages. LogTape currently provides a few
|
|
160
|
+
sinks: console and stream. However, you can easily add your own sinks.
|
|
161
|
+
The signature of a sink is:
|
|
162
|
+
|
|
163
|
+
~~~~ typescript
|
|
164
|
+
export type Sink = (record: LogRecord) => void;
|
|
165
|
+
~~~~
|
|
166
|
+
|
|
167
|
+
Here's a simple example of a sink that writes log messages to console:
|
|
168
|
+
|
|
169
|
+
~~~~ typescript
|
|
170
|
+
import { configure } from "@logtape/logtape";
|
|
171
|
+
|
|
172
|
+
configure({
|
|
173
|
+
sinks: {
|
|
174
|
+
console(record) {
|
|
175
|
+
console.log(record.message);
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
// Omitted for brevity
|
|
179
|
+
});
|
|
180
|
+
~~~~
|
|
181
|
+
|
|
182
|
+
### Console sink
|
|
183
|
+
|
|
184
|
+
Of course, you don't have to implement your own console sink because LogTape
|
|
185
|
+
provides a console sink:
|
|
186
|
+
|
|
187
|
+
~~~~ typescript
|
|
188
|
+
import { configure, getConsoleSink } from "@logtape/logtape";
|
|
189
|
+
|
|
190
|
+
configure({
|
|
191
|
+
sinks: {
|
|
192
|
+
console: getConsoleSink(),
|
|
193
|
+
},
|
|
194
|
+
// Omitted for brevity
|
|
195
|
+
});
|
|
196
|
+
~~~~
|
|
197
|
+
|
|
198
|
+
See also [`getConsoleSink()`] function and [`ConsoleSinkOptions`] interface
|
|
199
|
+
in the API reference for more details.
|
|
200
|
+
|
|
201
|
+
[`getConsoleSink()`]: https://jsr.io/@logtape/logtape/doc/~/getConsoleSink
|
|
202
|
+
[`ConsoleSinkOptions`]: https://jsr.io/@logtape/logtape/doc/~/ConsoleSinkOptions
|
|
203
|
+
|
|
204
|
+
### Stream sink
|
|
205
|
+
|
|
206
|
+
Another built-in sink is a stream sink. It writes log messages to
|
|
207
|
+
a [`WritableStream`]. Here's an example of a stream sink that writes log
|
|
208
|
+
messages to the standard error:
|
|
209
|
+
|
|
210
|
+
~~~~ typescript
|
|
211
|
+
// Deno:
|
|
212
|
+
configure({
|
|
213
|
+
sinks: {
|
|
214
|
+
stream: getStreamSink(Deno.stderr.writable),
|
|
215
|
+
},
|
|
216
|
+
// Omitted for brevity
|
|
217
|
+
});
|
|
218
|
+
~~~~
|
|
219
|
+
|
|
220
|
+
~~~~ typescript
|
|
221
|
+
// Node.js:
|
|
222
|
+
import stream from "node:stream";
|
|
223
|
+
|
|
224
|
+
configure({
|
|
225
|
+
sinks: {
|
|
226
|
+
stream: getStreamSink(stream.Writable.toWeb(process.stderr)),
|
|
227
|
+
},
|
|
228
|
+
// Omitted for brevity
|
|
229
|
+
});
|
|
230
|
+
~~~~
|
|
231
|
+
|
|
232
|
+
> [!NOTE]
|
|
233
|
+
> Here we use `WritableStream` from the Web Streams API. If you are using
|
|
234
|
+
> Node.js, you cannot directly pass `process.stderr` to `getStreamSink` because
|
|
235
|
+
> `process.stderr` is not a `WritableStream` but a [`Writable`], which is a
|
|
236
|
+
> Node.js stream. You can use [`Writable.toWeb()`] method to convert a Node.js
|
|
237
|
+
> stream to a `WritableStream`.
|
|
238
|
+
|
|
239
|
+
See also [`getStreamSink()`] function and [`StreamSinkOptions`] interface
|
|
240
|
+
in the API reference for more details.
|
|
241
|
+
|
|
242
|
+
[`WritableStream`]: https://developer.mozilla.org/en-US/docs/Web/API/WritableStream
|
|
243
|
+
[`Writable`]: https://nodejs.org/api/stream.html#class-streamwritable
|
|
244
|
+
[`Writable.toWeb()`]: https://nodejs.org/api/stream.html#streamwritabletowebstreamwritable
|
|
245
|
+
[`getStreamSink()`]: https://jsr.io/@logtape/logtape/doc/~/getStreamSink
|
|
246
|
+
[`StreamSinkOptions`]: https://jsr.io/@logtape/logtape/doc/~/StreamSinkOptions
|
|
247
|
+
|
|
248
|
+
### File sink
|
|
249
|
+
|
|
250
|
+
> [!NOTE]
|
|
251
|
+
> File sink is unavailable in the browser environment.
|
|
252
|
+
|
|
253
|
+
LogTape provides a file sink as well. Here's an example of a file sink that
|
|
254
|
+
writes log messages to a file:
|
|
255
|
+
|
|
256
|
+
~~~~ typescript
|
|
257
|
+
import { getFileSink } from "@logtape/logtape";
|
|
258
|
+
|
|
259
|
+
configure({
|
|
260
|
+
sinks: {
|
|
261
|
+
file: getFileSink("my-app.log"),
|
|
262
|
+
},
|
|
263
|
+
// Omitted for brevity
|
|
264
|
+
});
|
|
265
|
+
~~~~
|
|
266
|
+
|
|
267
|
+
See also [`getFileSink()`] function and [`FileSinkOptions`] interface
|
|
268
|
+
in the API reference for more details.
|
|
269
|
+
|
|
270
|
+
[`getFileSink()`]: https://jsr.io/@logtape/logtape/doc/~/getFileSink
|
|
271
|
+
[`FileSinkOptions`]: https://jsr.io/@logtape/logtape/doc/~/FileSinkOptions
|
|
272
|
+
|
|
273
|
+
### Text formatter
|
|
274
|
+
|
|
275
|
+
A stream sink and a file sink write log messages in a plain text format.
|
|
276
|
+
You can customize the format by providing a text formatter. The type of a
|
|
277
|
+
text formatter is:
|
|
278
|
+
|
|
279
|
+
~~~~ typescript
|
|
280
|
+
export type TextFormatter = (record: LogRecord) => string;
|
|
281
|
+
~~~~
|
|
282
|
+
|
|
283
|
+
Here's an example of a text formatter that writes log messages in a JSON format:
|
|
284
|
+
|
|
285
|
+
~~~~ typescript
|
|
286
|
+
configure({
|
|
287
|
+
sinks: {
|
|
288
|
+
stream: getStreamSink(Deno.stderr.writable, {
|
|
289
|
+
formatter: JSON.stringify,
|
|
290
|
+
}),
|
|
291
|
+
},
|
|
292
|
+
// Omitted for brevity
|
|
293
|
+
})
|
|
294
|
+
~~~~
|
|
295
|
+
|
|
296
|
+
### Disposable sink
|
|
297
|
+
|
|
298
|
+
A disposable sink is a sink that can be disposed of. They are automatically
|
|
299
|
+
disposed of when the configuration is reset or the program exits. The type
|
|
300
|
+
of a disposable sink is: `Sink & Disposable`. You can create a disposable
|
|
301
|
+
sink by defining a `[Symbol.dispose]` method:
|
|
302
|
+
|
|
303
|
+
~~~~ typescript
|
|
304
|
+
const disposableSink: Sink & Disposable = (record: LogRecord) => {
|
|
305
|
+
console.log(record.message);
|
|
306
|
+
};
|
|
307
|
+
disposableSink[Symbol.dispose] = () => {
|
|
308
|
+
console.log("Disposed!");
|
|
309
|
+
};
|
|
310
|
+
~~~~
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
Testing
|
|
314
|
+
-------
|
|
315
|
+
|
|
316
|
+
Here are some tips for testing your application or library with LogTape.
|
|
317
|
+
|
|
318
|
+
### Reset configuration
|
|
319
|
+
|
|
320
|
+
You can reset the configuration of LogTape to its initial state. This is
|
|
321
|
+
useful when you want to reset the configuration between tests. For example,
|
|
322
|
+
the following code shows how to reset the configuration after a test
|
|
323
|
+
(regardless of whether the test passes or fails) in Deno:
|
|
324
|
+
|
|
325
|
+
~~~~ typescript
|
|
326
|
+
import { configure, reset } from "@logtape/logtape";
|
|
327
|
+
|
|
328
|
+
Deno.test("my test", async (t) => {
|
|
329
|
+
await t.step("set up", () => {
|
|
330
|
+
configure({ /* ... */ });
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
await t.step("run test", () => {
|
|
334
|
+
// Run the test
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
await t.step("tear down", () => {
|
|
338
|
+
reset();
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
~~~~
|
|
342
|
+
|
|
343
|
+
### Buffer sink
|
|
344
|
+
|
|
345
|
+
For testing purposes, you may want to collect log messages in memory. Although
|
|
346
|
+
LogTape does not provide a built-in buffer sink, you can easily implement it:
|
|
347
|
+
|
|
348
|
+
~~~~ typescript
|
|
349
|
+
import { type LogRecord, configure } from "@logtape/logtape";
|
|
350
|
+
|
|
351
|
+
const buffer: LogRecord[] = [];
|
|
352
|
+
|
|
353
|
+
configure({
|
|
354
|
+
sinks: {
|
|
355
|
+
buffer: buffer.push.bind(buffer),
|
|
356
|
+
},
|
|
357
|
+
// Omitted for brevity
|
|
358
|
+
});
|
|
359
|
+
~~~~
|
package/esm/config.js
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
|
+
import * as dntShim from "./_dnt.shims.js";
|
|
1
2
|
import { toFilter } from "./filter.js";
|
|
2
3
|
import { LoggerImpl } from "./logger.js";
|
|
3
4
|
import { getConsoleSink } from "./sink.js";
|
|
5
|
+
/**
|
|
6
|
+
* Whether the loggers are configured.
|
|
7
|
+
*/
|
|
4
8
|
let configured = false;
|
|
9
|
+
/**
|
|
10
|
+
* Disposables to dispose when resetting the configuration.
|
|
11
|
+
*/
|
|
12
|
+
const disposables = new Set();
|
|
5
13
|
/**
|
|
6
14
|
* Configure the loggers with the specified configuration.
|
|
7
15
|
*
|
|
16
|
+
* Note that if the given sinks or filters are disposable, they will be
|
|
17
|
+
* disposed when the configuration is reset, or when the process exits.
|
|
18
|
+
*
|
|
8
19
|
* @example
|
|
9
20
|
* ```typescript
|
|
10
21
|
* configure({
|
|
@@ -42,8 +53,8 @@ export function configure(config) {
|
|
|
42
53
|
if (configured && !config.reset) {
|
|
43
54
|
throw new ConfigError("Already configured; if you want to reset, turn on the reset flag.");
|
|
44
55
|
}
|
|
56
|
+
reset();
|
|
45
57
|
configured = true;
|
|
46
|
-
LoggerImpl.getLogger([]).resetDescendants();
|
|
47
58
|
let metaConfigured = false;
|
|
48
59
|
for (const cfg of config.loggers) {
|
|
49
60
|
if (cfg.category.length === 0 ||
|
|
@@ -73,6 +84,20 @@ export function configure(config) {
|
|
|
73
84
|
logger.filters.push(toFilter(filter));
|
|
74
85
|
}
|
|
75
86
|
}
|
|
87
|
+
for (const sink of Object.values(config.sinks)) {
|
|
88
|
+
if (Symbol.dispose in sink)
|
|
89
|
+
disposables.add(sink);
|
|
90
|
+
}
|
|
91
|
+
for (const filter of Object.values(config.filters)) {
|
|
92
|
+
if (filter != null && typeof filter !== "string" && Symbol.dispose in filter)
|
|
93
|
+
disposables.add(filter);
|
|
94
|
+
}
|
|
95
|
+
if ("process" in dntShim.dntGlobalThis) { // @ts-ignore: It's fine to use process in Node
|
|
96
|
+
process.on("exit", dispose);
|
|
97
|
+
}
|
|
98
|
+
else { // @ts-ignore: It's fine to addEventListener() on the browser/Deno
|
|
99
|
+
addEventListener("unload", dispose);
|
|
100
|
+
}
|
|
76
101
|
const meta = LoggerImpl.getLogger(["logtape", "meta"]);
|
|
77
102
|
if (!metaConfigured) {
|
|
78
103
|
meta.sinks.push(getConsoleSink());
|
|
@@ -90,9 +115,18 @@ export function configure(config) {
|
|
|
90
115
|
* Reset the configuration. Mostly for testing purposes.
|
|
91
116
|
*/
|
|
92
117
|
export function reset() {
|
|
118
|
+
dispose();
|
|
93
119
|
LoggerImpl.getLogger([]).resetDescendants();
|
|
94
120
|
configured = false;
|
|
95
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Dispose of the disposables.
|
|
124
|
+
*/
|
|
125
|
+
export function dispose() {
|
|
126
|
+
for (const disposable of disposables)
|
|
127
|
+
disposable[Symbol.dispose]();
|
|
128
|
+
disposables.clear();
|
|
129
|
+
}
|
|
96
130
|
/**
|
|
97
131
|
* A configuration error.
|
|
98
132
|
*/
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as dntShim from "./_dnt.shims.js";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { webDriver } from "./filesink.web.js";
|
|
4
|
+
import { getFileSink as getBaseFileSink, } from "./sink.js";
|
|
5
|
+
/**
|
|
6
|
+
* A Node.js-specific file sink driver.
|
|
7
|
+
*/
|
|
8
|
+
export const nodeDriver = {
|
|
9
|
+
openSync(path) {
|
|
10
|
+
return fs.openSync(path, "a");
|
|
11
|
+
},
|
|
12
|
+
writeSync: fs.writeSync,
|
|
13
|
+
flushSync: fs.fsyncSync,
|
|
14
|
+
closeSync: fs.closeSync,
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Get a file sink.
|
|
18
|
+
*
|
|
19
|
+
* Note that this function is unavailable in the browser.
|
|
20
|
+
*
|
|
21
|
+
* @param path A path to the file to write to.
|
|
22
|
+
* @param options The options for the sink.
|
|
23
|
+
* @returns A sink that writes to the file. The sink is also a disposable
|
|
24
|
+
* object that closes the file when disposed.
|
|
25
|
+
*/
|
|
26
|
+
export function getFileSink(path, options = {}) {
|
|
27
|
+
if ("document" in dntShim.dntGlobalThis) {
|
|
28
|
+
return getBaseFileSink(path, { ...options, ...webDriver });
|
|
29
|
+
}
|
|
30
|
+
return getBaseFileSink(path, { ...options, ...nodeDriver });
|
|
31
|
+
}
|
|
32
|
+
// cSpell: ignore filesink
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
function notImplemented() {
|
|
2
|
+
throw new Error("File sink is not available in the browser.");
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* A browser-specific file sink driver. All methods throw an error.
|
|
6
|
+
*/
|
|
7
|
+
export const webDriver = {
|
|
8
|
+
openSync: notImplemented,
|
|
9
|
+
writeSync: notImplemented,
|
|
10
|
+
flushSync: notImplemented,
|
|
11
|
+
closeSync: notImplemented,
|
|
12
|
+
};
|
package/esm/formatter.js
CHANGED
|
@@ -29,7 +29,7 @@ const inspect = eval(`(
|
|
|
29
29
|
* The default text formatter. This formatter formats log records as follows:
|
|
30
30
|
*
|
|
31
31
|
* ```
|
|
32
|
-
* 2023-11-14 22:13:20.000 +00:00 [INF] Hello, world!
|
|
32
|
+
* 2023-11-14 22:13:20.000 +00:00 [INF] category·subcategory: Hello, world!
|
|
33
33
|
* ```
|
|
34
34
|
*
|
|
35
35
|
* @param record The log record to format.
|
|
@@ -44,7 +44,8 @@ export function defaultTextFormatter(record) {
|
|
|
44
44
|
else
|
|
45
45
|
msg += inspect(record.message[i]);
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
const category = record.category.join("\xb7");
|
|
48
|
+
return `${ts.toISOString().replace("T", " ").replace("Z", " +00:00")} [${levelAbbreviations[record.level]}] ${category}: ${msg}\n`;
|
|
48
49
|
}
|
|
49
50
|
/**
|
|
50
51
|
* The styles for the log level in the console.
|
|
@@ -52,9 +53,9 @@ export function defaultTextFormatter(record) {
|
|
|
52
53
|
const logLevelStyles = {
|
|
53
54
|
"debug": "background-color: gray; color: white;",
|
|
54
55
|
"info": "background-color: white; color: black;",
|
|
55
|
-
"warning": "background-color: orange;",
|
|
56
|
-
"error": "background-color: red;",
|
|
57
|
-
"fatal": "background-color: maroon;",
|
|
56
|
+
"warning": "background-color: orange; color: black;",
|
|
57
|
+
"error": "background-color: red; color: white;",
|
|
58
|
+
"fatal": "background-color: maroon; color: white;",
|
|
58
59
|
};
|
|
59
60
|
/**
|
|
60
61
|
* The default console formatter.
|
|
@@ -74,8 +75,11 @@ export function defaultConsoleFormatter(record) {
|
|
|
74
75
|
values.push(record.message[i]);
|
|
75
76
|
}
|
|
76
77
|
}
|
|
78
|
+
const date = new Date(record.timestamp);
|
|
79
|
+
const time = `${date.getUTCHours().toString().padStart(2, "0")}:${date.getUTCMinutes().toString().padStart(2, "0")}:${date.getUTCSeconds().toString().padStart(2, "0")}.${date.getUTCMilliseconds().toString().padStart(3, "0")}`;
|
|
77
80
|
return [
|
|
78
|
-
`%c${record.level
|
|
81
|
+
`%c${time} %c${levelAbbreviations[record.level]}%c %c${record.category.join("\xb7")} %c${msg}`,
|
|
82
|
+
"color: gray;",
|
|
79
83
|
logLevelStyles[record.level],
|
|
80
84
|
"background-color: default;",
|
|
81
85
|
"color: gray;",
|
package/esm/mod.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export { ConfigError, configure, } from "./config.js";
|
|
1
|
+
export { ConfigError, configure, reset, } from "./config.js";
|
|
2
|
+
export { getFileSink } from "./filesink.node.js";
|
|
2
3
|
export { getLevelFilter, toFilter, } from "./filter.js";
|
|
3
|
-
export { defaultConsoleFormatter, } from "./formatter.js";
|
|
4
|
+
export { defaultConsoleFormatter, defaultTextFormatter, } from "./formatter.js";
|
|
4
5
|
export { getLogger } from "./logger.js";
|
|
5
|
-
export { getConsoleSink } from "./sink.js";
|
|
6
|
+
export { getConsoleSink, getStreamSink, } from "./sink.js";
|
|
7
|
+
// cSpell: ignore filesink
|
package/esm/sink.js
CHANGED
|
@@ -20,28 +20,29 @@ import { defaultConsoleFormatter, defaultTextFormatter, } from "./formatter.js";
|
|
|
20
20
|
* ```
|
|
21
21
|
*
|
|
22
22
|
* @param stream The stream to write to.
|
|
23
|
-
* @param
|
|
24
|
-
* {@link defaultTextFormatter}.
|
|
25
|
-
* @param encoder The text encoder to use. Defaults to an instance of
|
|
26
|
-
* {@link TextEncoder}.
|
|
23
|
+
* @param options The options for the sink.
|
|
27
24
|
* @returns A sink that writes to the stream.
|
|
28
25
|
*/
|
|
29
|
-
export function getStreamSink(stream,
|
|
26
|
+
export function getStreamSink(stream, options = {}) {
|
|
27
|
+
const formatter = options.formatter ?? defaultTextFormatter;
|
|
28
|
+
const encoder = options.encoder ?? new TextEncoder();
|
|
30
29
|
const writer = stream.getWriter();
|
|
31
|
-
|
|
30
|
+
const sink = (record) => {
|
|
32
31
|
const bytes = encoder.encode(formatter(record));
|
|
33
32
|
writer.ready.then(() => writer.write(bytes));
|
|
34
33
|
};
|
|
34
|
+
sink[Symbol.dispose] = () => writer.close();
|
|
35
|
+
return sink;
|
|
35
36
|
}
|
|
36
37
|
/**
|
|
37
38
|
* A console sink factory that returns a sink that logs to the console.
|
|
38
39
|
*
|
|
39
|
-
* @param
|
|
40
|
-
* {@link defaultConsoleFormatter}.
|
|
41
|
-
* @param console The console to log to. Defaults to {@link console}.
|
|
40
|
+
* @param options The options for the sink.
|
|
42
41
|
* @returns A sink that logs to the console.
|
|
43
42
|
*/
|
|
44
|
-
export function getConsoleSink(
|
|
43
|
+
export function getConsoleSink(options = {}) {
|
|
44
|
+
const formatter = options.formatter ?? defaultConsoleFormatter;
|
|
45
|
+
const console = options.console ?? globalThis.console;
|
|
45
46
|
return (record) => {
|
|
46
47
|
const args = formatter(record);
|
|
47
48
|
if (record.level === "debug")
|
|
@@ -57,3 +58,23 @@ export function getConsoleSink(formatter = defaultConsoleFormatter, console = gl
|
|
|
57
58
|
throw new TypeError(`Invalid log level: ${record.level}.`);
|
|
58
59
|
};
|
|
59
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Get a platform-independent file sink.
|
|
63
|
+
*
|
|
64
|
+
* @typeParam TFile The type of the file descriptor.
|
|
65
|
+
* @param path A path to the file to write to.
|
|
66
|
+
* @param options The options for the sink and the file driver.
|
|
67
|
+
* @returns A sink that writes to the file. The sink is also a disposable
|
|
68
|
+
* object that closes the file when disposed.
|
|
69
|
+
*/
|
|
70
|
+
export function getFileSink(path, options) {
|
|
71
|
+
const formatter = options.formatter ?? defaultTextFormatter;
|
|
72
|
+
const encoder = options.encoder ?? new TextEncoder();
|
|
73
|
+
const fd = options.openSync(path);
|
|
74
|
+
const sink = (record) => {
|
|
75
|
+
options.writeSync(fd, encoder.encode(formatter(record)));
|
|
76
|
+
options.flushSync(fd);
|
|
77
|
+
};
|
|
78
|
+
sink[Symbol.dispose] = () => options.closeSync(fd);
|
|
79
|
+
return sink;
|
|
80
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logtape/logtape",
|
|
3
|
-
"version": "0.1.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Simple logging library for Deno/Node.js/Bun/browsers",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"logging",
|
|
7
|
+
"log",
|
|
8
|
+
"logger"
|
|
9
|
+
],
|
|
5
10
|
"author": {
|
|
6
11
|
"name": "Hong Minhee",
|
|
7
12
|
"email": "hong@minhee.org",
|
package/script/config.js
CHANGED
|
@@ -1,13 +1,47 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ConfigError = exports.reset = exports.configure = void 0;
|
|
26
|
+
exports.ConfigError = exports.dispose = exports.reset = exports.configure = void 0;
|
|
27
|
+
const dntShim = __importStar(require("./_dnt.shims.js"));
|
|
4
28
|
const filter_js_1 = require("./filter.js");
|
|
5
29
|
const logger_js_1 = require("./logger.js");
|
|
6
30
|
const sink_js_1 = require("./sink.js");
|
|
31
|
+
/**
|
|
32
|
+
* Whether the loggers are configured.
|
|
33
|
+
*/
|
|
7
34
|
let configured = false;
|
|
35
|
+
/**
|
|
36
|
+
* Disposables to dispose when resetting the configuration.
|
|
37
|
+
*/
|
|
38
|
+
const disposables = new Set();
|
|
8
39
|
/**
|
|
9
40
|
* Configure the loggers with the specified configuration.
|
|
10
41
|
*
|
|
42
|
+
* Note that if the given sinks or filters are disposable, they will be
|
|
43
|
+
* disposed when the configuration is reset, or when the process exits.
|
|
44
|
+
*
|
|
11
45
|
* @example
|
|
12
46
|
* ```typescript
|
|
13
47
|
* configure({
|
|
@@ -45,8 +79,8 @@ function configure(config) {
|
|
|
45
79
|
if (configured && !config.reset) {
|
|
46
80
|
throw new ConfigError("Already configured; if you want to reset, turn on the reset flag.");
|
|
47
81
|
}
|
|
82
|
+
reset();
|
|
48
83
|
configured = true;
|
|
49
|
-
logger_js_1.LoggerImpl.getLogger([]).resetDescendants();
|
|
50
84
|
let metaConfigured = false;
|
|
51
85
|
for (const cfg of config.loggers) {
|
|
52
86
|
if (cfg.category.length === 0 ||
|
|
@@ -76,6 +110,20 @@ function configure(config) {
|
|
|
76
110
|
logger.filters.push((0, filter_js_1.toFilter)(filter));
|
|
77
111
|
}
|
|
78
112
|
}
|
|
113
|
+
for (const sink of Object.values(config.sinks)) {
|
|
114
|
+
if (Symbol.dispose in sink)
|
|
115
|
+
disposables.add(sink);
|
|
116
|
+
}
|
|
117
|
+
for (const filter of Object.values(config.filters)) {
|
|
118
|
+
if (filter != null && typeof filter !== "string" && Symbol.dispose in filter)
|
|
119
|
+
disposables.add(filter);
|
|
120
|
+
}
|
|
121
|
+
if ("process" in dntShim.dntGlobalThis) { // @ts-ignore: It's fine to use process in Node
|
|
122
|
+
process.on("exit", dispose);
|
|
123
|
+
}
|
|
124
|
+
else { // @ts-ignore: It's fine to addEventListener() on the browser/Deno
|
|
125
|
+
addEventListener("unload", dispose);
|
|
126
|
+
}
|
|
79
127
|
const meta = logger_js_1.LoggerImpl.getLogger(["logtape", "meta"]);
|
|
80
128
|
if (!metaConfigured) {
|
|
81
129
|
meta.sinks.push((0, sink_js_1.getConsoleSink)());
|
|
@@ -94,10 +142,20 @@ exports.configure = configure;
|
|
|
94
142
|
* Reset the configuration. Mostly for testing purposes.
|
|
95
143
|
*/
|
|
96
144
|
function reset() {
|
|
145
|
+
dispose();
|
|
97
146
|
logger_js_1.LoggerImpl.getLogger([]).resetDescendants();
|
|
98
147
|
configured = false;
|
|
99
148
|
}
|
|
100
149
|
exports.reset = reset;
|
|
150
|
+
/**
|
|
151
|
+
* Dispose of the disposables.
|
|
152
|
+
*/
|
|
153
|
+
function dispose() {
|
|
154
|
+
for (const disposable of disposables)
|
|
155
|
+
disposable[Symbol.dispose]();
|
|
156
|
+
disposables.clear();
|
|
157
|
+
}
|
|
158
|
+
exports.dispose = dispose;
|
|
101
159
|
/**
|
|
102
160
|
* A configuration error.
|
|
103
161
|
*/
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.getFileSink = exports.nodeDriver = void 0;
|
|
30
|
+
const dntShim = __importStar(require("./_dnt.shims.js"));
|
|
31
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
32
|
+
const filesink_web_js_1 = require("./filesink.web.js");
|
|
33
|
+
const sink_js_1 = require("./sink.js");
|
|
34
|
+
/**
|
|
35
|
+
* A Node.js-specific file sink driver.
|
|
36
|
+
*/
|
|
37
|
+
exports.nodeDriver = {
|
|
38
|
+
openSync(path) {
|
|
39
|
+
return node_fs_1.default.openSync(path, "a");
|
|
40
|
+
},
|
|
41
|
+
writeSync: node_fs_1.default.writeSync,
|
|
42
|
+
flushSync: node_fs_1.default.fsyncSync,
|
|
43
|
+
closeSync: node_fs_1.default.closeSync,
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Get a file sink.
|
|
47
|
+
*
|
|
48
|
+
* Note that this function is unavailable in the browser.
|
|
49
|
+
*
|
|
50
|
+
* @param path A path to the file to write to.
|
|
51
|
+
* @param options The options for the sink.
|
|
52
|
+
* @returns A sink that writes to the file. The sink is also a disposable
|
|
53
|
+
* object that closes the file when disposed.
|
|
54
|
+
*/
|
|
55
|
+
function getFileSink(path, options = {}) {
|
|
56
|
+
if ("document" in dntShim.dntGlobalThis) {
|
|
57
|
+
return (0, sink_js_1.getFileSink)(path, { ...options, ...filesink_web_js_1.webDriver });
|
|
58
|
+
}
|
|
59
|
+
return (0, sink_js_1.getFileSink)(path, { ...options, ...exports.nodeDriver });
|
|
60
|
+
}
|
|
61
|
+
exports.getFileSink = getFileSink;
|
|
62
|
+
// cSpell: ignore filesink
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.webDriver = void 0;
|
|
4
|
+
function notImplemented() {
|
|
5
|
+
throw new Error("File sink is not available in the browser.");
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A browser-specific file sink driver. All methods throw an error.
|
|
9
|
+
*/
|
|
10
|
+
exports.webDriver = {
|
|
11
|
+
openSync: notImplemented,
|
|
12
|
+
writeSync: notImplemented,
|
|
13
|
+
flushSync: notImplemented,
|
|
14
|
+
closeSync: notImplemented,
|
|
15
|
+
};
|
package/script/formatter.js
CHANGED
|
@@ -32,7 +32,7 @@ const inspect = eval(`(
|
|
|
32
32
|
* The default text formatter. This formatter formats log records as follows:
|
|
33
33
|
*
|
|
34
34
|
* ```
|
|
35
|
-
* 2023-11-14 22:13:20.000 +00:00 [INF] Hello, world!
|
|
35
|
+
* 2023-11-14 22:13:20.000 +00:00 [INF] category·subcategory: Hello, world!
|
|
36
36
|
* ```
|
|
37
37
|
*
|
|
38
38
|
* @param record The log record to format.
|
|
@@ -47,7 +47,8 @@ function defaultTextFormatter(record) {
|
|
|
47
47
|
else
|
|
48
48
|
msg += inspect(record.message[i]);
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
const category = record.category.join("\xb7");
|
|
51
|
+
return `${ts.toISOString().replace("T", " ").replace("Z", " +00:00")} [${levelAbbreviations[record.level]}] ${category}: ${msg}\n`;
|
|
51
52
|
}
|
|
52
53
|
exports.defaultTextFormatter = defaultTextFormatter;
|
|
53
54
|
/**
|
|
@@ -56,9 +57,9 @@ exports.defaultTextFormatter = defaultTextFormatter;
|
|
|
56
57
|
const logLevelStyles = {
|
|
57
58
|
"debug": "background-color: gray; color: white;",
|
|
58
59
|
"info": "background-color: white; color: black;",
|
|
59
|
-
"warning": "background-color: orange;",
|
|
60
|
-
"error": "background-color: red;",
|
|
61
|
-
"fatal": "background-color: maroon;",
|
|
60
|
+
"warning": "background-color: orange; color: black;",
|
|
61
|
+
"error": "background-color: red; color: white;",
|
|
62
|
+
"fatal": "background-color: maroon; color: white;",
|
|
62
63
|
};
|
|
63
64
|
/**
|
|
64
65
|
* The default console formatter.
|
|
@@ -78,8 +79,11 @@ function defaultConsoleFormatter(record) {
|
|
|
78
79
|
values.push(record.message[i]);
|
|
79
80
|
}
|
|
80
81
|
}
|
|
82
|
+
const date = new Date(record.timestamp);
|
|
83
|
+
const time = `${date.getUTCHours().toString().padStart(2, "0")}:${date.getUTCMinutes().toString().padStart(2, "0")}:${date.getUTCSeconds().toString().padStart(2, "0")}.${date.getUTCMilliseconds().toString().padStart(3, "0")}`;
|
|
81
84
|
return [
|
|
82
|
-
`%c${record.level
|
|
85
|
+
`%c${time} %c${levelAbbreviations[record.level]}%c %c${record.category.join("\xb7")} %c${msg}`,
|
|
86
|
+
"color: gray;",
|
|
83
87
|
logLevelStyles[record.level],
|
|
84
88
|
"background-color: default;",
|
|
85
89
|
"color: gray;",
|
package/script/mod.js
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getConsoleSink = exports.getLogger = exports.defaultConsoleFormatter = exports.toFilter = exports.getLevelFilter = exports.configure = exports.ConfigError = void 0;
|
|
3
|
+
exports.getStreamSink = exports.getConsoleSink = exports.getLogger = exports.defaultTextFormatter = exports.defaultConsoleFormatter = exports.toFilter = exports.getLevelFilter = exports.getFileSink = exports.reset = exports.configure = exports.ConfigError = void 0;
|
|
4
4
|
var config_js_1 = require("./config.js");
|
|
5
5
|
Object.defineProperty(exports, "ConfigError", { enumerable: true, get: function () { return config_js_1.ConfigError; } });
|
|
6
6
|
Object.defineProperty(exports, "configure", { enumerable: true, get: function () { return config_js_1.configure; } });
|
|
7
|
+
Object.defineProperty(exports, "reset", { enumerable: true, get: function () { return config_js_1.reset; } });
|
|
8
|
+
var filesink_node_js_1 = require("./filesink.node.js");
|
|
9
|
+
Object.defineProperty(exports, "getFileSink", { enumerable: true, get: function () { return filesink_node_js_1.getFileSink; } });
|
|
7
10
|
var filter_js_1 = require("./filter.js");
|
|
8
11
|
Object.defineProperty(exports, "getLevelFilter", { enumerable: true, get: function () { return filter_js_1.getLevelFilter; } });
|
|
9
12
|
Object.defineProperty(exports, "toFilter", { enumerable: true, get: function () { return filter_js_1.toFilter; } });
|
|
10
13
|
var formatter_js_1 = require("./formatter.js");
|
|
11
14
|
Object.defineProperty(exports, "defaultConsoleFormatter", { enumerable: true, get: function () { return formatter_js_1.defaultConsoleFormatter; } });
|
|
15
|
+
Object.defineProperty(exports, "defaultTextFormatter", { enumerable: true, get: function () { return formatter_js_1.defaultTextFormatter; } });
|
|
12
16
|
var logger_js_1 = require("./logger.js");
|
|
13
17
|
Object.defineProperty(exports, "getLogger", { enumerable: true, get: function () { return logger_js_1.getLogger; } });
|
|
14
18
|
var sink_js_1 = require("./sink.js");
|
|
15
19
|
Object.defineProperty(exports, "getConsoleSink", { enumerable: true, get: function () { return sink_js_1.getConsoleSink; } });
|
|
20
|
+
Object.defineProperty(exports, "getStreamSink", { enumerable: true, get: function () { return sink_js_1.getStreamSink; } });
|
|
21
|
+
// cSpell: ignore filesink
|
package/script/sink.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getConsoleSink = exports.getStreamSink = void 0;
|
|
3
|
+
exports.getFileSink = exports.getConsoleSink = exports.getStreamSink = void 0;
|
|
4
4
|
const formatter_js_1 = require("./formatter.js");
|
|
5
5
|
/**
|
|
6
6
|
* A factory that returns a sink that writes to a {@link WritableStream}.
|
|
@@ -23,29 +23,30 @@ const formatter_js_1 = require("./formatter.js");
|
|
|
23
23
|
* ```
|
|
24
24
|
*
|
|
25
25
|
* @param stream The stream to write to.
|
|
26
|
-
* @param
|
|
27
|
-
* {@link defaultTextFormatter}.
|
|
28
|
-
* @param encoder The text encoder to use. Defaults to an instance of
|
|
29
|
-
* {@link TextEncoder}.
|
|
26
|
+
* @param options The options for the sink.
|
|
30
27
|
* @returns A sink that writes to the stream.
|
|
31
28
|
*/
|
|
32
|
-
function getStreamSink(stream,
|
|
29
|
+
function getStreamSink(stream, options = {}) {
|
|
30
|
+
const formatter = options.formatter ?? formatter_js_1.defaultTextFormatter;
|
|
31
|
+
const encoder = options.encoder ?? new TextEncoder();
|
|
33
32
|
const writer = stream.getWriter();
|
|
34
|
-
|
|
33
|
+
const sink = (record) => {
|
|
35
34
|
const bytes = encoder.encode(formatter(record));
|
|
36
35
|
writer.ready.then(() => writer.write(bytes));
|
|
37
36
|
};
|
|
37
|
+
sink[Symbol.dispose] = () => writer.close();
|
|
38
|
+
return sink;
|
|
38
39
|
}
|
|
39
40
|
exports.getStreamSink = getStreamSink;
|
|
40
41
|
/**
|
|
41
42
|
* A console sink factory that returns a sink that logs to the console.
|
|
42
43
|
*
|
|
43
|
-
* @param
|
|
44
|
-
* {@link defaultConsoleFormatter}.
|
|
45
|
-
* @param console The console to log to. Defaults to {@link console}.
|
|
44
|
+
* @param options The options for the sink.
|
|
46
45
|
* @returns A sink that logs to the console.
|
|
47
46
|
*/
|
|
48
|
-
function getConsoleSink(
|
|
47
|
+
function getConsoleSink(options = {}) {
|
|
48
|
+
const formatter = options.formatter ?? formatter_js_1.defaultConsoleFormatter;
|
|
49
|
+
const console = options.console ?? globalThis.console;
|
|
49
50
|
return (record) => {
|
|
50
51
|
const args = formatter(record);
|
|
51
52
|
if (record.level === "debug")
|
|
@@ -62,3 +63,24 @@ function getConsoleSink(formatter = formatter_js_1.defaultConsoleFormatter, cons
|
|
|
62
63
|
};
|
|
63
64
|
}
|
|
64
65
|
exports.getConsoleSink = getConsoleSink;
|
|
66
|
+
/**
|
|
67
|
+
* Get a platform-independent file sink.
|
|
68
|
+
*
|
|
69
|
+
* @typeParam TFile The type of the file descriptor.
|
|
70
|
+
* @param path A path to the file to write to.
|
|
71
|
+
* @param options The options for the sink and the file driver.
|
|
72
|
+
* @returns A sink that writes to the file. The sink is also a disposable
|
|
73
|
+
* object that closes the file when disposed.
|
|
74
|
+
*/
|
|
75
|
+
function getFileSink(path, options) {
|
|
76
|
+
const formatter = options.formatter ?? formatter_js_1.defaultTextFormatter;
|
|
77
|
+
const encoder = options.encoder ?? new TextEncoder();
|
|
78
|
+
const fd = options.openSync(path);
|
|
79
|
+
const sink = (record) => {
|
|
80
|
+
options.writeSync(fd, encoder.encode(formatter(record)));
|
|
81
|
+
options.flushSync(fd);
|
|
82
|
+
};
|
|
83
|
+
sink[Symbol.dispose] = () => options.closeSync(fd);
|
|
84
|
+
return sink;
|
|
85
|
+
}
|
|
86
|
+
exports.getFileSink = getFileSink;
|
package/types/config.d.ts
CHANGED
|
@@ -50,6 +50,9 @@ export interface LoggerConfig<TSinkId extends string, TFilterId extends string>
|
|
|
50
50
|
/**
|
|
51
51
|
* Configure the loggers with the specified configuration.
|
|
52
52
|
*
|
|
53
|
+
* Note that if the given sinks or filters are disposable, they will be
|
|
54
|
+
* disposed when the configuration is reset, or when the process exits.
|
|
55
|
+
*
|
|
53
56
|
* @example
|
|
54
57
|
* ```typescript
|
|
55
58
|
* configure({
|
|
@@ -88,6 +91,10 @@ export declare function configure<TSinkId extends string, TFilterId extends stri
|
|
|
88
91
|
* Reset the configuration. Mostly for testing purposes.
|
|
89
92
|
*/
|
|
90
93
|
export declare function reset(): void;
|
|
94
|
+
/**
|
|
95
|
+
* Dispose of the disposables.
|
|
96
|
+
*/
|
|
97
|
+
export declare function dispose(): void;
|
|
91
98
|
/**
|
|
92
99
|
* A configuration error.
|
|
93
100
|
*/
|
package/types/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,UAAU,EAAY,MAAM,aAAa,CAAC;AAExD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAkB,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtD;;GAEG;AACH,MAAM,WAAW,MAAM,CAAC,OAAO,SAAS,MAAM,EAAE,SAAS,SAAS,MAAM;IACtE;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC7B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAEvC;;OAEG;IACH,OAAO,EAAE,YAAY,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;IAE5C;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAC3B,OAAO,SAAS,MAAM,EACtB,SAAS,SAAS,MAAM;IAExB;;;OAGG;IACH,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAE5B;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAElB;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC;IAEtB;;;OAGG;IACH,KAAK,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CACzB;AAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,SAAS,CAAC,OAAO,SAAS,MAAM,EAAE,SAAS,SAAS,MAAM,EACxE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,QA0EnC;AAED;;GAEG;AACH,wBAAgB,KAAK,SAIpB;AAED;;GAEG;AACH,wBAAgB,OAAO,SAGtB;AAED;;GAEG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC;;;OAGG;gBACS,OAAO,EAAE,MAAM;CAI5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../../../../src/deps/deno.land/x/which_runtime@0.2.0/mod.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,MAAM,SAAgC,CAAC;AACpD,eAAO,MAAM,MAAM,SAA0B,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { type FileSinkDriver, type FileSinkOptions, type Sink } from "./sink.js";
|
|
3
|
+
/**
|
|
4
|
+
* A Node.js-specific file sink driver.
|
|
5
|
+
*/
|
|
6
|
+
export declare const nodeDriver: FileSinkDriver<number>;
|
|
7
|
+
/**
|
|
8
|
+
* Get a file sink.
|
|
9
|
+
*
|
|
10
|
+
* Note that this function is unavailable in the browser.
|
|
11
|
+
*
|
|
12
|
+
* @param path A path to the file to write to.
|
|
13
|
+
* @param options The options for the sink.
|
|
14
|
+
* @returns A sink that writes to the file. The sink is also a disposable
|
|
15
|
+
* object that closes the file when disposed.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getFileSink(path: string, options?: FileSinkOptions): Sink & Disposable;
|
|
18
|
+
//# sourceMappingURL=filesink.node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesink.node.d.ts","sourceRoot":"","sources":["../src/filesink.node.ts"],"names":[],"mappings":";AAGA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,eAAe,EAEpB,KAAK,IAAI,EACV,MAAM,WAAW,CAAC;AAEnB;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,cAAc,CAAC,MAAM,CAO7C,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,eAAoB,GAC5B,IAAI,GAAG,UAAU,CAKnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesink.test.d.ts","sourceRoot":"","sources":["../src/filesink.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesink.web.d.ts","sourceRoot":"","sources":["../src/filesink.web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMhD;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,cAAc,CAAC,IAAI,CAK1C,CAAC"}
|
package/types/formatter.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type TextFormatter = (record: LogRecord) => string;
|
|
|
11
11
|
* The default text formatter. This formatter formats log records as follows:
|
|
12
12
|
*
|
|
13
13
|
* ```
|
|
14
|
-
* 2023-11-14 22:13:20.000 +00:00 [INF] Hello, world!
|
|
14
|
+
* 2023-11-14 22:13:20.000 +00:00 [INF] category·subcategory: Hello, world!
|
|
15
15
|
* ```
|
|
16
16
|
*
|
|
17
17
|
* @param record The log record to format.
|
package/types/formatter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,SAAS,EAAE,MAAM,aAAa,CAAC;AAEvD;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,SAAS,KAAK,MAAM,CAAC;AA+B1D;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,SAAS,EAAE,MAAM,aAAa,CAAC;AAEvD;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,SAAS,KAAK,MAAM,CAAC;AA+B1D;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAW9D;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,SAAS,KAAK,SAAS,OAAO,EAAE,CAAC;AAazE;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,OAAO,EAAE,CA2B7E"}
|
package/types/mod.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export { type Config, ConfigError, configure, type LoggerConfig, } from "./config.js";
|
|
1
|
+
export { type Config, ConfigError, configure, type LoggerConfig, reset, } from "./config.js";
|
|
2
|
+
export { getFileSink } from "./filesink.node.js";
|
|
2
3
|
export { type Filter, type FilterLike, getLevelFilter, toFilter, } from "./filter.js";
|
|
3
|
-
export { type ConsoleFormatter, defaultConsoleFormatter, type TextFormatter, } from "./formatter.js";
|
|
4
|
+
export { type ConsoleFormatter, defaultConsoleFormatter, defaultTextFormatter, type TextFormatter, } from "./formatter.js";
|
|
4
5
|
export { getLogger, type Logger } from "./logger.js";
|
|
5
6
|
export type { LogLevel, LogRecord } from "./record.js";
|
|
6
|
-
export { getConsoleSink, type Sink } from "./sink.js";
|
|
7
|
+
export { type ConsoleSinkOptions, type FileSinkDriver, type FileSinkOptions, getConsoleSink, getStreamSink, type Sink, type StreamSinkOptions, } from "./sink.js";
|
|
7
8
|
//# sourceMappingURL=mod.d.ts.map
|
package/types/mod.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,WAAW,EACX,SAAS,EACT,KAAK,YAAY,
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,WAAW,EACX,SAAS,EACT,KAAK,YAAY,EACjB,KAAK,GACN,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EACL,KAAK,MAAM,EACX,KAAK,UAAU,EACf,cAAc,EACd,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,gBAAgB,EACrB,uBAAuB,EACvB,oBAAoB,EACpB,KAAK,aAAa,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACrD,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,cAAc,EACd,aAAa,EACb,KAAK,IAAI,EACT,KAAK,iBAAiB,GACvB,MAAM,WAAW,CAAC"}
|
package/types/sink.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
/// <reference types="node" />
|
|
3
|
+
/// <reference types="node" />
|
|
3
4
|
import * as dntShim from "./_dnt.shims.js";
|
|
4
5
|
import { type ConsoleFormatter, type TextFormatter } from "./formatter.js";
|
|
5
6
|
import type { LogRecord } from "./record.js";
|
|
@@ -13,6 +14,21 @@ import type { LogRecord } from "./record.js";
|
|
|
13
14
|
* @param record The log record to sink.
|
|
14
15
|
*/
|
|
15
16
|
export type Sink = (record: LogRecord) => void;
|
|
17
|
+
/**
|
|
18
|
+
* Options for the {@link getStreamSink} function.
|
|
19
|
+
*/
|
|
20
|
+
export interface StreamSinkOptions {
|
|
21
|
+
/**
|
|
22
|
+
* The text formatter to use. Defaults to {@link defaultTextFormatter}.
|
|
23
|
+
*/
|
|
24
|
+
formatter?: TextFormatter;
|
|
25
|
+
/**
|
|
26
|
+
* The text encoder to use. Defaults to an instance of {@link TextEncoder}.
|
|
27
|
+
*/
|
|
28
|
+
encoder?: {
|
|
29
|
+
encode(text: string): Uint8Array;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
16
32
|
/**
|
|
17
33
|
* A factory that returns a sink that writes to a {@link WritableStream}.
|
|
18
34
|
*
|
|
@@ -34,22 +50,69 @@ export type Sink = (record: LogRecord) => void;
|
|
|
34
50
|
* ```
|
|
35
51
|
*
|
|
36
52
|
* @param stream The stream to write to.
|
|
37
|
-
* @param
|
|
38
|
-
* {@link defaultTextFormatter}.
|
|
39
|
-
* @param encoder The text encoder to use. Defaults to an instance of
|
|
40
|
-
* {@link TextEncoder}.
|
|
53
|
+
* @param options The options for the sink.
|
|
41
54
|
* @returns A sink that writes to the stream.
|
|
42
55
|
*/
|
|
43
|
-
export declare function getStreamSink(stream: dntShim.WritableStream,
|
|
44
|
-
|
|
45
|
-
}
|
|
56
|
+
export declare function getStreamSink(stream: dntShim.WritableStream, options?: StreamSinkOptions): Sink & Disposable;
|
|
57
|
+
/**
|
|
58
|
+
* Options for the {@link getConsoleSink} function.
|
|
59
|
+
*/
|
|
60
|
+
export interface ConsoleSinkOptions {
|
|
61
|
+
/**
|
|
62
|
+
* The console formatter to use. Defaults to {@link defaultConsoleFormatter}.
|
|
63
|
+
*/
|
|
64
|
+
formatter?: ConsoleFormatter;
|
|
65
|
+
/**
|
|
66
|
+
* The console to log to. Defaults to {@link console}.
|
|
67
|
+
*/
|
|
68
|
+
console?: Console;
|
|
69
|
+
}
|
|
46
70
|
/**
|
|
47
71
|
* A console sink factory that returns a sink that logs to the console.
|
|
48
72
|
*
|
|
49
|
-
* @param
|
|
50
|
-
* {@link defaultConsoleFormatter}.
|
|
51
|
-
* @param console The console to log to. Defaults to {@link console}.
|
|
73
|
+
* @param options The options for the sink.
|
|
52
74
|
* @returns A sink that logs to the console.
|
|
53
75
|
*/
|
|
54
|
-
export declare function getConsoleSink(
|
|
76
|
+
export declare function getConsoleSink(options?: ConsoleSinkOptions): Sink;
|
|
77
|
+
/**
|
|
78
|
+
* Options for the {@link getFileSink} function.
|
|
79
|
+
*/
|
|
80
|
+
export type FileSinkOptions = StreamSinkOptions;
|
|
81
|
+
/**
|
|
82
|
+
* A platform-specific file sink driver.
|
|
83
|
+
* @typeParam TFile The type of the file descriptor.
|
|
84
|
+
*/
|
|
85
|
+
export interface FileSinkDriver<TFile> {
|
|
86
|
+
/**
|
|
87
|
+
* Open a file for appending and return a file descriptor.
|
|
88
|
+
* @param path A path to the file to open.
|
|
89
|
+
*/
|
|
90
|
+
openSync(path: string): TFile;
|
|
91
|
+
/**
|
|
92
|
+
* Write a chunk of data to the file.
|
|
93
|
+
* @param fd The file descriptor.
|
|
94
|
+
* @param chunk The data to write.
|
|
95
|
+
*/
|
|
96
|
+
writeSync(fd: TFile, chunk: Uint8Array): void;
|
|
97
|
+
/**
|
|
98
|
+
* Flush the file to ensure that all data is written to the disk.
|
|
99
|
+
* @param fd The file descriptor.
|
|
100
|
+
*/
|
|
101
|
+
flushSync(fd: TFile): void;
|
|
102
|
+
/**
|
|
103
|
+
* Close the file.
|
|
104
|
+
* @param fd The file descriptor.
|
|
105
|
+
*/
|
|
106
|
+
closeSync(fd: TFile): void;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get a platform-independent file sink.
|
|
110
|
+
*
|
|
111
|
+
* @typeParam TFile The type of the file descriptor.
|
|
112
|
+
* @param path A path to the file to write to.
|
|
113
|
+
* @param options The options for the sink and the file driver.
|
|
114
|
+
* @returns A sink that writes to the file. The sink is also a disposable
|
|
115
|
+
* object that closes the file when disposed.
|
|
116
|
+
*/
|
|
117
|
+
export declare function getFileSink<TFile>(path: string, options: FileSinkOptions & FileSinkDriver<TFile>): Sink & Disposable;
|
|
55
118
|
//# sourceMappingURL=sink.d.ts.map
|
package/types/sink.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sink.d.ts","sourceRoot":"","sources":["../src/sink.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sink.d.ts","sourceRoot":"","sources":["../src/sink.ts"],"names":[],"mappings":";;;AAAA,OAAO,KAAK,OAAO,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EACL,KAAK,gBAAgB,EAGrB,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;;;;GAQG;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC;IAE1B;;OAEG;IACH,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAC;CAChD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,OAAO,CAAC,cAAc,EAC9B,OAAO,GAAE,iBAAsB,GAC9B,IAAI,GAAG,UAAU,CAUnB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAE7B;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,kBAAuB,GAAG,IAAI,CAYrE;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,iBAAiB,CAAC;AAEhD;;;GAGG;AACH,MAAM,WAAW,cAAc,CAAC,KAAK;IACnC;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IAE9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAE9C;;;OAGG;IACH,SAAS,CAAC,EAAE,EAAE,KAAK,GAAG,IAAI,CAAC;IAE3B;;;OAGG;IACH,SAAS,CAAC,EAAE,EAAE,KAAK,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,eAAe,GAAG,cAAc,CAAC,KAAK,CAAC,GAC/C,IAAI,GAAG,UAAU,CAUnB"}
|