loggily 0.6.2 → 0.7.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 +73 -31
- package/dist/context.d.mts +91 -0
- package/dist/context.d.mts.map +1 -0
- package/dist/context.mjs +145 -0
- package/dist/context.mjs.map +1 -0
- package/dist/core-DAFH-huv.d.mts +199 -0
- package/dist/core-DAFH-huv.d.mts.map +1 -0
- package/dist/core-Du3sIje6.mjs +900 -0
- package/dist/core-Du3sIje6.mjs.map +1 -0
- package/dist/index-Co4jC3mx.d.mts +98 -0
- package/dist/index-Co4jC3mx.d.mts.map +1 -0
- package/dist/index.d.mts +3 -333
- package/dist/index.mjs +2 -734
- package/dist/metrics.d.mts +48 -0
- package/dist/metrics.d.mts.map +1 -0
- package/dist/metrics.mjs +130 -0
- package/dist/metrics.mjs.map +1 -0
- package/dist/worker.d.mts +173 -0
- package/dist/worker.d.mts.map +1 -0
- package/dist/worker.mjs +471 -0
- package/dist/worker.mjs.map +1 -0
- package/package.json +25 -8
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -2,21 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
**Clarity without the clutter.**
|
|
4
4
|
|
|
5
|
-
Debugs, logs, and spans
|
|
5
|
+
Debugs, logs, and spans -- one API.
|
|
6
6
|
|
|
7
7
|
[](https://github.com/beorn/loggily/actions/workflows/test.yml)
|
|
8
8
|
[](https://www.npmjs.com/package/loggily)
|
|
9
9
|
[](https://bundlephobia.com/package/loggily)
|
|
10
10
|
[](LICENSE)
|
|
11
11
|
|
|
12
|
-
Most apps end up with three logging tools: `debug` for local troubleshooting, a JSON logger
|
|
12
|
+
Most apps end up with three logging tools: `debug` for local troubleshooting, a JSON logger for production, and ad-hoc timers or a tracing SDK for performance. Three APIs, three configs, three output formats.
|
|
13
13
|
|
|
14
14
|
Loggily replaces all three with one namespace tree and one output pipeline. Pure TypeScript, zero dependencies, ~3 KB.
|
|
15
15
|
|
|
16
16
|
```typescript
|
|
17
17
|
import { createLogger } from "loggily"
|
|
18
18
|
|
|
19
|
-
const log = createLogger("myapp")
|
|
19
|
+
const log = createLogger("myapp", [{ level: "debug" }, console])
|
|
20
20
|
|
|
21
21
|
log.info?.("server started", { port: 3000 })
|
|
22
22
|
log.debug?.("cache hit", { key: "user:42" })
|
|
@@ -63,26 +63,42 @@ In benchmarks with expensive disabled log arguments, this is [~22x faster](https
|
|
|
63
63
|
npm install loggily
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
| Requirement
|
|
67
|
-
|
|
68
|
-
| Node.js
|
|
69
|
-
| Bun
|
|
70
|
-
| TypeScript
|
|
71
|
-
| Module format | ESM-only
|
|
72
|
-
| Browser
|
|
66
|
+
| Requirement | Version |
|
|
67
|
+
| ------------- | ----------------------------------------------- |
|
|
68
|
+
| Node.js | >= 23.6 |
|
|
69
|
+
| Bun | 1.0+ |
|
|
70
|
+
| TypeScript | 5.2+ for `using`; `.end()` works on any version |
|
|
71
|
+
| Module format | ESM-only |
|
|
72
|
+
| Browser | Supported via conditional export |
|
|
73
73
|
|
|
74
74
|
Loggily uses `Symbol.dispose` (TC39 Explicit Resource Management) for span cleanup, which requires a modern runtime.
|
|
75
75
|
|
|
76
76
|
## Features
|
|
77
77
|
|
|
78
|
-
- **
|
|
79
|
-
- **
|
|
80
|
-
- **
|
|
81
|
-
- **
|
|
82
|
-
- **
|
|
83
|
-
- **
|
|
84
|
-
- **
|
|
85
|
-
- **
|
|
78
|
+
- **Config pipeline** -- second arg to `createLogger` is a config array: objects configure (`{ level, ns, format }`), arrays branch, values write. Pass `console` for terminal output, `{ file: "/path" }` for file output, or functions for custom stages.
|
|
79
|
+
- **Namespace hierarchy** -- organize logs with `:` separators. `DEBUG=myapp:db` shows only database output, compatible with the same patterns as the `debug` package.
|
|
80
|
+
- **Lightweight spans** -- time any operation with `using span = log.span("name")`. Automatic duration, parent-child tracking, and trace IDs.
|
|
81
|
+
- **Dev & production** -- colorized console in development, structured JSON in production. Same code, zero config.
|
|
82
|
+
- **Child context** -- `log.child({ requestId })` adds structured fields to every message in the chain.
|
|
83
|
+
- **Automatic async context** -- enable `AsyncLocalStorage`-based propagation and every log in a request's async chain inherits trace/span IDs without passing loggers around.
|
|
84
|
+
- **Lazy messages** -- `log.debug?.(() => expensiveString())` skips the function entirely when disabled.
|
|
85
|
+
- **Error overloads** -- `log.error?.(err)`, `log.error?.(err, "msg")`, and `log.error?.(err, "msg", data)`.
|
|
86
|
+
- **Worker threads** -- forward logs from workers to the main thread with full type safety.
|
|
87
|
+
|
|
88
|
+
### Config Array
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { createLogger } from "loggily"
|
|
92
|
+
|
|
93
|
+
// Objects configure, arrays branch, values write
|
|
94
|
+
const log = createLogger("myapp", [
|
|
95
|
+
{ level: "debug", ns: "-sql" },
|
|
96
|
+
console,
|
|
97
|
+
{ file: "/tmp/app.log", level: "error", format: "json" },
|
|
98
|
+
])
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
When no config array is provided, loggily reads `LOG_LEVEL`, `DEBUG`, `LOG_FORMAT`, and `NODE_ENV` from the environment.
|
|
86
102
|
|
|
87
103
|
### Spans
|
|
88
104
|
|
|
@@ -96,20 +112,46 @@ Loggily uses `Symbol.dispose` (TC39 Explicit Resource Management) for span clean
|
|
|
96
112
|
|
|
97
113
|
// Without `using` — call .end() manually
|
|
98
114
|
const span = log.span("db:query")
|
|
99
|
-
try {
|
|
115
|
+
try {
|
|
116
|
+
/* ... */
|
|
117
|
+
} finally {
|
|
118
|
+
span.end()
|
|
119
|
+
}
|
|
100
120
|
```
|
|
101
121
|
|
|
102
122
|
### Common configuration
|
|
103
123
|
|
|
104
|
-
| Variable
|
|
105
|
-
|
|
106
|
-
| `DEBUG`
|
|
107
|
-
| `LOG_LEVEL`
|
|
108
|
-
| `LOG_FORMAT` | `console`, `json`
|
|
109
|
-
| `TRACE`
|
|
124
|
+
| Variable | Example | Effect |
|
|
125
|
+
| ------------ | ------------------------- | ------------------------------------------------------ |
|
|
126
|
+
| `DEBUG` | `myapp:db,-myapp:sql` | Namespace filter (compatible with the `debug` package) |
|
|
127
|
+
| `LOG_LEVEL` | `debug`, `info`, `warn` | Minimum output level |
|
|
128
|
+
| `LOG_FORMAT` | `console`, `json` | Override output format |
|
|
129
|
+
| `TRACE` | `1` or namespace prefixes | Enable span output |
|
|
110
130
|
|
|
111
131
|
See the [full environment variable reference](https://beorn.codes/loggily/api/configuration).
|
|
112
132
|
|
|
133
|
+
### Types
|
|
134
|
+
|
|
135
|
+
Key types exported for power users:
|
|
136
|
+
|
|
137
|
+
| Type | Description |
|
|
138
|
+
| ------------------- | ---------------------------------------------------------------- |
|
|
139
|
+
| `LogEvent` | A log message event (kind, level, namespace, message, props) |
|
|
140
|
+
| `SpanEvent` | A span timing event (kind, namespace, duration, spanId, traceId) |
|
|
141
|
+
| `Event` | `LogEvent \| SpanEvent` |
|
|
142
|
+
| `Stage` | `(event: Event) => Event \| null \| void` |
|
|
143
|
+
| `Pipeline` | `{ dispatch, level, dispose }` |
|
|
144
|
+
| `ConditionalLogger` | Logger with `?.`-compatible methods |
|
|
145
|
+
|
|
146
|
+
`buildPipeline()` and `defaultPipeline()` are exported for direct pipeline construction.
|
|
147
|
+
|
|
148
|
+
## Compatibility
|
|
149
|
+
|
|
150
|
+
- **`DEBUG=` compatible** -- uses the same namespace filter patterns as the `debug` package
|
|
151
|
+
- **Works with Pino transports** -- custom stage functions can forward events to any sink
|
|
152
|
+
- **W3C Trace Context** -- `traceparent()` generates standard trace headers
|
|
153
|
+
- **OpenTelemetry compatible** -- span events include `spanId`, `traceId`, `parentId`
|
|
154
|
+
|
|
113
155
|
## Why this exists
|
|
114
156
|
|
|
115
157
|
Loggily was built while developing a terminal UI where disabled debug logs inside the render loop were eating frame time. No existing logger solved the "disabled calls should cost nothing" problem at the language level, so `?.` became the foundation.
|
|
@@ -118,15 +160,15 @@ Loggily was built while developing a terminal UI where disabled debug logs insid
|
|
|
118
160
|
|
|
119
161
|
## When not to use Loggily
|
|
120
162
|
|
|
121
|
-
- **You need
|
|
122
|
-
- **You need distributed tracing with vendor exporters and auto-instrumentation.**
|
|
163
|
+
- **You need worker-thread transport pipelines with log rotation and dozens of plugins.** [Pino](https://getpino.io/) has a mature transport ecosystem for this.
|
|
164
|
+
- **You need distributed tracing with vendor exporters and auto-instrumentation.** [OpenTelemetry](https://opentelemetry.io/) is the industry standard.
|
|
123
165
|
|
|
124
166
|
## Documentation
|
|
125
167
|
|
|
126
|
-
- **[Get Started](https://beorn.codes/loggily/guide/journey)**
|
|
127
|
-
- **[Full docs site](https://beorn.codes/loggily/)**
|
|
128
|
-
- [Comparison](https://beorn.codes/loggily/guide/comparison)
|
|
129
|
-
- [Migration from debug](https://beorn.codes/loggily/guide/migration-from-debug)
|
|
168
|
+
- **[Get Started](https://beorn.codes/loggily/guide/journey)** -- progressive guide from first log to full observability
|
|
169
|
+
- **[Full docs site](https://beorn.codes/loggily/)** -- guides, API reference, migration guides
|
|
170
|
+
- [Comparison](https://beorn.codes/loggily/guide/comparison) -- what Loggily does, compatibility, when to use something else
|
|
171
|
+
- [Migration from debug](https://beorn.codes/loggily/guide/migration-from-debug) -- step-by-step migration guide
|
|
130
172
|
|
|
131
173
|
## License
|
|
132
174
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
//#region src/context.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* AsyncLocalStorage-based context propagation for loggily — Node.js/Bun only.
|
|
4
|
+
*
|
|
5
|
+
* Separated from core logger to allow tree-shaking in browser bundles.
|
|
6
|
+
* When enabled, new spans automatically parent to the current context span,
|
|
7
|
+
* and writeLog() auto-tags with trace_id/span_id from context.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { enableContextPropagation, getCurrentSpan } from "loggily/context"
|
|
12
|
+
*
|
|
13
|
+
* enableContextPropagation()
|
|
14
|
+
*
|
|
15
|
+
* const log = createLogger("myapp")
|
|
16
|
+
* {
|
|
17
|
+
* using span = log.span("request")
|
|
18
|
+
* // All logs and child spans within this async context
|
|
19
|
+
* // automatically inherit trace_id and span_id
|
|
20
|
+
* log.info("inside span") // auto-tagged with trace_id, span_id
|
|
21
|
+
*
|
|
22
|
+
* const current = getCurrentSpan()
|
|
23
|
+
* // current === { spanId: "sp_1", traceId: "tr_1", parentId: null }
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
/** Minimal span context stored in AsyncLocalStorage */
|
|
28
|
+
interface SpanContext {
|
|
29
|
+
readonly spanId: string;
|
|
30
|
+
readonly traceId: string;
|
|
31
|
+
readonly parentId: string | null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Enable AsyncLocalStorage-based context propagation.
|
|
35
|
+
* Once enabled, new spans automatically parent to the current context span,
|
|
36
|
+
* and log messages are auto-tagged with trace_id/span_id.
|
|
37
|
+
*
|
|
38
|
+
* **Node.js/Bun only** — not available in browser environments.
|
|
39
|
+
*/
|
|
40
|
+
declare function enableContextPropagation(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Disable context propagation.
|
|
43
|
+
* Existing spans continue to work, but new spans won't auto-parent.
|
|
44
|
+
*/
|
|
45
|
+
declare function disableContextPropagation(): void;
|
|
46
|
+
/** Check if context propagation is enabled */
|
|
47
|
+
declare function isContextPropagationEnabled(): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Get the current span context from AsyncLocalStorage.
|
|
50
|
+
* Returns null if no span is active in the current async context,
|
|
51
|
+
* or if context propagation is not enabled.
|
|
52
|
+
*/
|
|
53
|
+
declare function getCurrentSpan(): SpanContext | null;
|
|
54
|
+
/**
|
|
55
|
+
* Enter a span context for the remainder of the current synchronous execution
|
|
56
|
+
* and any async operations started from it. Used by the logger when creating
|
|
57
|
+
* spans with `using` — since `using` doesn't wrap user code in a callback,
|
|
58
|
+
* `enterWith()` is the right primitive.
|
|
59
|
+
*
|
|
60
|
+
* Captures the full previous SpanContext snapshot so it can be restored
|
|
61
|
+
* exactly on exit, even with non-LIFO end() ordering.
|
|
62
|
+
*
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
declare function enterSpanContext(spanId: string, traceId: string, parentId: string | null): void;
|
|
66
|
+
/**
|
|
67
|
+
* Restore the previous span context (called when a span ends).
|
|
68
|
+
* Restores the exact SpanContext snapshot captured at enter time,
|
|
69
|
+
* preventing corruption from non-LIFO end() ordering.
|
|
70
|
+
*
|
|
71
|
+
* @internal
|
|
72
|
+
*/
|
|
73
|
+
declare function exitSpanContext(spanId: string): void;
|
|
74
|
+
/**
|
|
75
|
+
* Run a function within a span context.
|
|
76
|
+
* Used for explicit context scoping (e.g., in request handlers).
|
|
77
|
+
*
|
|
78
|
+
* @param context - The span context to set
|
|
79
|
+
* @param fn - The function to run within the context
|
|
80
|
+
* @returns The return value of fn
|
|
81
|
+
*/
|
|
82
|
+
declare function runInSpanContext<T>(context: SpanContext, fn: () => T): T;
|
|
83
|
+
/**
|
|
84
|
+
* Get the context tags (trace_id, span_id) for the current async context.
|
|
85
|
+
* Used by writeLog() to auto-tag log messages.
|
|
86
|
+
* Returns empty object if context propagation is disabled or no span is active.
|
|
87
|
+
*/
|
|
88
|
+
declare function getContextTags(): Record<string, string>;
|
|
89
|
+
//#endregion
|
|
90
|
+
export { SpanContext, disableContextPropagation, enableContextPropagation, enterSpanContext, exitSpanContext, getContextTags, getCurrentSpan, isContextPropagationEnabled, runInSpanContext };
|
|
91
|
+
//# sourceMappingURL=context.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.mts","names":[],"sources":["../src/context.ts"],"mappings":";;AAgCA;;;;;;;;;AA2BA;;;;;AAuBA;;;;;AAMA;;;;;AASA;AAAA,UAjEiB,WAAA;EAAA,SACN,MAAA;EAAA,SACA,OAAA;EAAA,SACA,QAAA;AAAA;;;;;;;;iBAwBK,wBAAA,CAAA;AAuEhB;;;;AAAA,iBAhDgB,yBAAA,CAAA;AAsEhB;AAAA,iBAhEgB,2BAAA,CAAA;;;;;;iBASA,cAAA,CAAA,GAAkB,WAAA;;;;;;;;;AAiElC;;;iBAjDgB,gBAAA,CAAiB,MAAA,UAAgB,OAAA,UAAiB,QAAA;;;;;;;;iBAiBlD,eAAA,CAAgB,MAAA;;;;;;;;;iBAsBhB,gBAAA,GAAA,CAAoB,OAAA,EAAS,WAAA,EAAa,EAAA,QAAU,CAAA,GAAI,CAAA;;;;;;iBAUxD,cAAA,CAAA,GAAkB,MAAA"}
|
package/dist/context.mjs
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { i as _setContextHooks, n as _clearContextHooks } from "./core-Du3sIje6.mjs";
|
|
2
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3
|
+
//#region src/context.ts
|
|
4
|
+
/**
|
|
5
|
+
* AsyncLocalStorage-based context propagation for loggily — Node.js/Bun only.
|
|
6
|
+
*
|
|
7
|
+
* Separated from core logger to allow tree-shaking in browser bundles.
|
|
8
|
+
* When enabled, new spans automatically parent to the current context span,
|
|
9
|
+
* and writeLog() auto-tags with trace_id/span_id from context.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { enableContextPropagation, getCurrentSpan } from "loggily/context"
|
|
14
|
+
*
|
|
15
|
+
* enableContextPropagation()
|
|
16
|
+
*
|
|
17
|
+
* const log = createLogger("myapp")
|
|
18
|
+
* {
|
|
19
|
+
* using span = log.span("request")
|
|
20
|
+
* // All logs and child spans within this async context
|
|
21
|
+
* // automatically inherit trace_id and span_id
|
|
22
|
+
* log.info("inside span") // auto-tagged with trace_id, span_id
|
|
23
|
+
*
|
|
24
|
+
* const current = getCurrentSpan()
|
|
25
|
+
* // current === { spanId: "sp_1", traceId: "tr_1", parentId: null }
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
let storage = null;
|
|
30
|
+
let contextEnabled = false;
|
|
31
|
+
/**
|
|
32
|
+
* Map from spanId → the SpanContext that was active when the span was entered.
|
|
33
|
+
* Used to restore the exact previous context on exit, avoiding corruption
|
|
34
|
+
* from non-LIFO end() ordering.
|
|
35
|
+
*/
|
|
36
|
+
const previousContexts = /* @__PURE__ */ new Map();
|
|
37
|
+
/**
|
|
38
|
+
* Enable AsyncLocalStorage-based context propagation.
|
|
39
|
+
* Once enabled, new spans automatically parent to the current context span,
|
|
40
|
+
* and log messages are auto-tagged with trace_id/span_id.
|
|
41
|
+
*
|
|
42
|
+
* **Node.js/Bun only** — not available in browser environments.
|
|
43
|
+
*/
|
|
44
|
+
function enableContextPropagation() {
|
|
45
|
+
if (!storage) storage = new AsyncLocalStorage();
|
|
46
|
+
contextEnabled = true;
|
|
47
|
+
_setContextHooks({
|
|
48
|
+
getContextTags,
|
|
49
|
+
getContextParent() {
|
|
50
|
+
const span = getCurrentSpan();
|
|
51
|
+
if (!span) return null;
|
|
52
|
+
return {
|
|
53
|
+
spanId: span.spanId,
|
|
54
|
+
traceId: span.traceId
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
enterContext: enterSpanContext,
|
|
58
|
+
exitContext: exitSpanContext
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Disable context propagation.
|
|
63
|
+
* Existing spans continue to work, but new spans won't auto-parent.
|
|
64
|
+
*/
|
|
65
|
+
function disableContextPropagation() {
|
|
66
|
+
contextEnabled = false;
|
|
67
|
+
_clearContextHooks();
|
|
68
|
+
}
|
|
69
|
+
/** Check if context propagation is enabled */
|
|
70
|
+
function isContextPropagationEnabled() {
|
|
71
|
+
return contextEnabled;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get the current span context from AsyncLocalStorage.
|
|
75
|
+
* Returns null if no span is active in the current async context,
|
|
76
|
+
* or if context propagation is not enabled.
|
|
77
|
+
*/
|
|
78
|
+
function getCurrentSpan() {
|
|
79
|
+
if (!contextEnabled || !storage) return null;
|
|
80
|
+
return storage.getStore() ?? null;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Enter a span context for the remainder of the current synchronous execution
|
|
84
|
+
* and any async operations started from it. Used by the logger when creating
|
|
85
|
+
* spans with `using` — since `using` doesn't wrap user code in a callback,
|
|
86
|
+
* `enterWith()` is the right primitive.
|
|
87
|
+
*
|
|
88
|
+
* Captures the full previous SpanContext snapshot so it can be restored
|
|
89
|
+
* exactly on exit, even with non-LIFO end() ordering.
|
|
90
|
+
*
|
|
91
|
+
* @internal
|
|
92
|
+
*/
|
|
93
|
+
function enterSpanContext(spanId, traceId, parentId) {
|
|
94
|
+
if (!contextEnabled || !storage) return;
|
|
95
|
+
const previous = storage.getStore() ?? null;
|
|
96
|
+
previousContexts.set(spanId, previous);
|
|
97
|
+
storage.enterWith({
|
|
98
|
+
spanId,
|
|
99
|
+
traceId,
|
|
100
|
+
parentId
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Restore the previous span context (called when a span ends).
|
|
105
|
+
* Restores the exact SpanContext snapshot captured at enter time,
|
|
106
|
+
* preventing corruption from non-LIFO end() ordering.
|
|
107
|
+
*
|
|
108
|
+
* @internal
|
|
109
|
+
*/
|
|
110
|
+
function exitSpanContext(spanId) {
|
|
111
|
+
if (!contextEnabled || !storage) return;
|
|
112
|
+
const previous = previousContexts.get(spanId);
|
|
113
|
+
previousContexts.delete(spanId);
|
|
114
|
+
if (previous) storage.enterWith(previous);
|
|
115
|
+
else storage.enterWith(void 0);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Run a function within a span context.
|
|
119
|
+
* Used for explicit context scoping (e.g., in request handlers).
|
|
120
|
+
*
|
|
121
|
+
* @param context - The span context to set
|
|
122
|
+
* @param fn - The function to run within the context
|
|
123
|
+
* @returns The return value of fn
|
|
124
|
+
*/
|
|
125
|
+
function runInSpanContext(context, fn) {
|
|
126
|
+
if (!contextEnabled || !storage) return fn();
|
|
127
|
+
return storage.run(context, fn);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get the context tags (trace_id, span_id) for the current async context.
|
|
131
|
+
* Used by writeLog() to auto-tag log messages.
|
|
132
|
+
* Returns empty object if context propagation is disabled or no span is active.
|
|
133
|
+
*/
|
|
134
|
+
function getContextTags() {
|
|
135
|
+
const span = getCurrentSpan();
|
|
136
|
+
if (!span) return {};
|
|
137
|
+
return {
|
|
138
|
+
trace_id: span.traceId,
|
|
139
|
+
span_id: span.spanId
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//#endregion
|
|
143
|
+
export { disableContextPropagation, enableContextPropagation, enterSpanContext, exitSpanContext, getContextTags, getCurrentSpan, isContextPropagationEnabled, runInSpanContext };
|
|
144
|
+
|
|
145
|
+
//# sourceMappingURL=context.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.mjs","names":[],"sources":["../src/context.ts"],"sourcesContent":["/**\n * AsyncLocalStorage-based context propagation for loggily — Node.js/Bun only.\n *\n * Separated from core logger to allow tree-shaking in browser bundles.\n * When enabled, new spans automatically parent to the current context span,\n * and writeLog() auto-tags with trace_id/span_id from context.\n *\n * @example\n * ```typescript\n * import { enableContextPropagation, getCurrentSpan } from \"loggily/context\"\n *\n * enableContextPropagation()\n *\n * const log = createLogger(\"myapp\")\n * {\n * using span = log.span(\"request\")\n * // All logs and child spans within this async context\n * // automatically inherit trace_id and span_id\n * log.info(\"inside span\") // auto-tagged with trace_id, span_id\n *\n * const current = getCurrentSpan()\n * // current === { spanId: \"sp_1\", traceId: \"tr_1\", parentId: null }\n * }\n * ```\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\"\nimport { _setContextHooks, _clearContextHooks } from \"./core.js\"\n\n// ============ Types ============\n\n/** Minimal span context stored in AsyncLocalStorage */\nexport interface SpanContext {\n readonly spanId: string\n readonly traceId: string\n readonly parentId: string | null\n}\n\n// ============ State ============\n\nlet storage: AsyncLocalStorage<SpanContext> | null = null\nlet contextEnabled = false\n\n/**\n * Map from spanId → the SpanContext that was active when the span was entered.\n * Used to restore the exact previous context on exit, avoiding corruption\n * from non-LIFO end() ordering.\n */\nconst previousContexts = new Map<string, SpanContext | null>()\n\n// ============ API ============\n\n/**\n * Enable AsyncLocalStorage-based context propagation.\n * Once enabled, new spans automatically parent to the current context span,\n * and log messages are auto-tagged with trace_id/span_id.\n *\n * **Node.js/Bun only** — not available in browser environments.\n */\nexport function enableContextPropagation(): void {\n if (!storage) {\n storage = new AsyncLocalStorage<SpanContext>()\n }\n contextEnabled = true\n\n // Register hooks with core.ts\n _setContextHooks({\n getContextTags,\n getContextParent() {\n const span = getCurrentSpan()\n if (!span) return null\n return { spanId: span.spanId, traceId: span.traceId }\n },\n enterContext: enterSpanContext,\n exitContext: exitSpanContext,\n })\n}\n\n/**\n * Disable context propagation.\n * Existing spans continue to work, but new spans won't auto-parent.\n */\nexport function disableContextPropagation(): void {\n contextEnabled = false\n _clearContextHooks()\n}\n\n/** Check if context propagation is enabled */\nexport function isContextPropagationEnabled(): boolean {\n return contextEnabled\n}\n\n/**\n * Get the current span context from AsyncLocalStorage.\n * Returns null if no span is active in the current async context,\n * or if context propagation is not enabled.\n */\nexport function getCurrentSpan(): SpanContext | null {\n if (!contextEnabled || !storage) return null\n return storage.getStore() ?? null\n}\n\n/**\n * Enter a span context for the remainder of the current synchronous execution\n * and any async operations started from it. Used by the logger when creating\n * spans with `using` — since `using` doesn't wrap user code in a callback,\n * `enterWith()` is the right primitive.\n *\n * Captures the full previous SpanContext snapshot so it can be restored\n * exactly on exit, even with non-LIFO end() ordering.\n *\n * @internal\n */\nexport function enterSpanContext(spanId: string, traceId: string, parentId: string | null): void {\n if (!contextEnabled || !storage) return\n\n // Capture the full previous context before overwriting\n const previous = storage.getStore() ?? null\n previousContexts.set(spanId, previous)\n\n storage.enterWith({ spanId, traceId, parentId })\n}\n\n/**\n * Restore the previous span context (called when a span ends).\n * Restores the exact SpanContext snapshot captured at enter time,\n * preventing corruption from non-LIFO end() ordering.\n *\n * @internal\n */\nexport function exitSpanContext(spanId: string): void {\n if (!contextEnabled || !storage) return\n\n const previous = previousContexts.get(spanId)\n previousContexts.delete(spanId)\n\n if (previous) {\n storage.enterWith(previous)\n } else {\n // No previous context — exit entirely\n storage.enterWith(undefined as unknown as SpanContext)\n }\n}\n\n/**\n * Run a function within a span context.\n * Used for explicit context scoping (e.g., in request handlers).\n *\n * @param context - The span context to set\n * @param fn - The function to run within the context\n * @returns The return value of fn\n */\nexport function runInSpanContext<T>(context: SpanContext, fn: () => T): T {\n if (!contextEnabled || !storage) return fn()\n return storage.run(context, fn)\n}\n\n/**\n * Get the context tags (trace_id, span_id) for the current async context.\n * Used by writeLog() to auto-tag log messages.\n * Returns empty object if context propagation is disabled or no span is active.\n */\nexport function getContextTags(): Record<string, string> {\n const span = getCurrentSpan()\n if (!span) return {}\n return {\n trace_id: span.traceId,\n span_id: span.spanId,\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,IAAI,UAAiD;AACrD,IAAI,iBAAiB;;;;;;AAOrB,MAAM,mCAAmB,IAAI,KAAiC;;;;;;;;AAW9D,SAAgB,2BAAiC;AAC/C,KAAI,CAAC,QACH,WAAU,IAAI,mBAAgC;AAEhD,kBAAiB;AAGjB,kBAAiB;EACf;EACA,mBAAmB;GACjB,MAAM,OAAO,gBAAgB;AAC7B,OAAI,CAAC,KAAM,QAAO;AAClB,UAAO;IAAE,QAAQ,KAAK;IAAQ,SAAS,KAAK;IAAS;;EAEvD,cAAc;EACd,aAAa;EACd,CAAC;;;;;;AAOJ,SAAgB,4BAAkC;AAChD,kBAAiB;AACjB,qBAAoB;;;AAItB,SAAgB,8BAAuC;AACrD,QAAO;;;;;;;AAQT,SAAgB,iBAAqC;AACnD,KAAI,CAAC,kBAAkB,CAAC,QAAS,QAAO;AACxC,QAAO,QAAQ,UAAU,IAAI;;;;;;;;;;;;;AAc/B,SAAgB,iBAAiB,QAAgB,SAAiB,UAA+B;AAC/F,KAAI,CAAC,kBAAkB,CAAC,QAAS;CAGjC,MAAM,WAAW,QAAQ,UAAU,IAAI;AACvC,kBAAiB,IAAI,QAAQ,SAAS;AAEtC,SAAQ,UAAU;EAAE;EAAQ;EAAS;EAAU,CAAC;;;;;;;;;AAUlD,SAAgB,gBAAgB,QAAsB;AACpD,KAAI,CAAC,kBAAkB,CAAC,QAAS;CAEjC,MAAM,WAAW,iBAAiB,IAAI,OAAO;AAC7C,kBAAiB,OAAO,OAAO;AAE/B,KAAI,SACF,SAAQ,UAAU,SAAS;KAG3B,SAAQ,UAAU,KAAA,EAAoC;;;;;;;;;;AAY1D,SAAgB,iBAAoB,SAAsB,IAAgB;AACxE,KAAI,CAAC,kBAAkB,CAAC,QAAS,QAAO,IAAI;AAC5C,QAAO,QAAQ,IAAI,SAAS,GAAG;;;;;;;AAQjC,SAAgB,iBAAyC;CACvD,MAAM,OAAO,gBAAgB;AAC7B,KAAI,CAAC,KAAM,QAAO,EAAE;AACpB,QAAO;EACL,UAAU,KAAK;EACf,SAAS,KAAK;EACf"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
//#region src/pipeline.d.ts
|
|
2
|
+
type OutputLogLevel = "trace" | "debug" | "info" | "warn" | "error";
|
|
3
|
+
type LogLevel = OutputLogLevel | "silent";
|
|
4
|
+
type LogFormat = "console" | "json";
|
|
5
|
+
type LogEvent = {
|
|
6
|
+
kind: "log";
|
|
7
|
+
time: number;
|
|
8
|
+
namespace: string;
|
|
9
|
+
level: OutputLogLevel;
|
|
10
|
+
message: string;
|
|
11
|
+
props?: Record<string, unknown>;
|
|
12
|
+
};
|
|
13
|
+
type SpanEvent = {
|
|
14
|
+
kind: "span";
|
|
15
|
+
time: number;
|
|
16
|
+
namespace: string;
|
|
17
|
+
name: string;
|
|
18
|
+
duration: number;
|
|
19
|
+
props?: Record<string, unknown>;
|
|
20
|
+
spanId: string;
|
|
21
|
+
traceId: string;
|
|
22
|
+
parentId: string | null;
|
|
23
|
+
};
|
|
24
|
+
type Event = LogEvent | SpanEvent;
|
|
25
|
+
type Stage = (event: Event) => Event | null | void;
|
|
26
|
+
declare const LOG_LEVEL_PRIORITY: Record<LogLevel, number>;
|
|
27
|
+
declare function safeStringify(value: unknown): string;
|
|
28
|
+
type NsFilter = (namespace: string) => boolean;
|
|
29
|
+
interface Pipeline {
|
|
30
|
+
dispatch: (event: Event) => void;
|
|
31
|
+
level: LogLevel;
|
|
32
|
+
dispose: () => void;
|
|
33
|
+
}
|
|
34
|
+
interface ScopeConfig {
|
|
35
|
+
level: LogLevel;
|
|
36
|
+
ns: NsFilter | null;
|
|
37
|
+
format: LogFormat;
|
|
38
|
+
}
|
|
39
|
+
declare function buildPipeline(elements: unknown[], parentConfig?: Partial<ScopeConfig>): Pipeline;
|
|
40
|
+
declare function defaultPipeline(): Pipeline;
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/core.d.ts
|
|
43
|
+
interface SpanRecord {
|
|
44
|
+
readonly name: string;
|
|
45
|
+
readonly durationMs: number;
|
|
46
|
+
}
|
|
47
|
+
interface SpanRecorder {
|
|
48
|
+
recordSpan(data: SpanRecord): void;
|
|
49
|
+
}
|
|
50
|
+
/** @internal */
|
|
51
|
+
declare let _ambientRecorder: SpanRecorder | null;
|
|
52
|
+
declare function _setAmbientRecorder(recorder: SpanRecorder | null): void;
|
|
53
|
+
type LazyMessage = string | (() => string);
|
|
54
|
+
type LazyProps = Record<string, unknown> | (() => Record<string, unknown>);
|
|
55
|
+
interface SpanData {
|
|
56
|
+
readonly id: string;
|
|
57
|
+
readonly traceId: string;
|
|
58
|
+
readonly parentId: string | null;
|
|
59
|
+
readonly startTime: number;
|
|
60
|
+
readonly endTime: number | null;
|
|
61
|
+
readonly duration: number | null;
|
|
62
|
+
[key: string]: unknown;
|
|
63
|
+
}
|
|
64
|
+
interface Logger {
|
|
65
|
+
readonly name: string;
|
|
66
|
+
readonly props: Readonly<Record<string, unknown>>;
|
|
67
|
+
readonly spanData: SpanData | null;
|
|
68
|
+
trace(message: LazyMessage, data?: Record<string, unknown>): void;
|
|
69
|
+
debug(message: LazyMessage, data?: Record<string, unknown>): void;
|
|
70
|
+
info(message: LazyMessage, data?: Record<string, unknown>): void;
|
|
71
|
+
warn(message: LazyMessage, data?: Record<string, unknown>): void;
|
|
72
|
+
error(message: LazyMessage, data?: Record<string, unknown>): void;
|
|
73
|
+
error(error: Error, data?: Record<string, unknown>): void;
|
|
74
|
+
error(error: Error, message: string, data?: Record<string, unknown>): void;
|
|
75
|
+
logger(namespace?: string, props?: Record<string, unknown>): ConditionalLogger;
|
|
76
|
+
span(namespace?: string, props?: LazyProps): SpanLogger;
|
|
77
|
+
child(context: Record<string, unknown>): ConditionalLogger;
|
|
78
|
+
/** @deprecated Use .logger() instead for namespace-based children */
|
|
79
|
+
child(context: string): ConditionalLogger;
|
|
80
|
+
end(): void;
|
|
81
|
+
}
|
|
82
|
+
interface SpanLogger extends Logger, Disposable {
|
|
83
|
+
readonly spanData: SpanData & {
|
|
84
|
+
[key: string]: unknown;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
interface ConditionalLogger {
|
|
88
|
+
readonly name: string;
|
|
89
|
+
readonly props: Readonly<Record<string, unknown>>;
|
|
90
|
+
readonly spanData: SpanData | null;
|
|
91
|
+
trace?: (message: LazyMessage, data?: Record<string, unknown>) => void;
|
|
92
|
+
debug?: (message: LazyMessage, data?: Record<string, unknown>) => void;
|
|
93
|
+
info?: (message: LazyMessage, data?: Record<string, unknown>) => void;
|
|
94
|
+
warn?: (message: LazyMessage, data?: Record<string, unknown>) => void;
|
|
95
|
+
error?: {
|
|
96
|
+
(message: LazyMessage, data?: Record<string, unknown>): void;
|
|
97
|
+
(error: Error, data?: Record<string, unknown>): void;
|
|
98
|
+
(error: Error, message: string, data?: Record<string, unknown>): void;
|
|
99
|
+
};
|
|
100
|
+
logger(namespace?: string, props?: Record<string, unknown>): ConditionalLogger;
|
|
101
|
+
span(namespace?: string, props?: LazyProps): SpanLogger;
|
|
102
|
+
child(context: Record<string, unknown>): ConditionalLogger;
|
|
103
|
+
child(context: string): ConditionalLogger;
|
|
104
|
+
end(): void;
|
|
105
|
+
}
|
|
106
|
+
declare function resetIds(): void;
|
|
107
|
+
/** @internal */
|
|
108
|
+
declare function _setContextHooks(hooks: {
|
|
109
|
+
getContextTags: () => Record<string, string>;
|
|
110
|
+
getContextParent: () => {
|
|
111
|
+
spanId: string;
|
|
112
|
+
traceId: string;
|
|
113
|
+
} | null;
|
|
114
|
+
enterContext: (spanId: string, traceId: string, parentId: string | null) => void;
|
|
115
|
+
exitContext: (spanId: string) => void;
|
|
116
|
+
}): void;
|
|
117
|
+
/** @internal */
|
|
118
|
+
declare function _clearContextHooks(): void;
|
|
119
|
+
interface SpanDataFields {
|
|
120
|
+
id: string;
|
|
121
|
+
traceId: string;
|
|
122
|
+
parentId: string | null;
|
|
123
|
+
startTime: number;
|
|
124
|
+
endTime: number | null;
|
|
125
|
+
duration: number | null;
|
|
126
|
+
}
|
|
127
|
+
declare function createSpanDataProxy(getFields: () => SpanDataFields, attrs: Record<string, unknown>): SpanData;
|
|
128
|
+
declare function startCollecting(): void;
|
|
129
|
+
declare function stopCollecting(): SpanData[];
|
|
130
|
+
declare function getCollectedSpans(): SpanData[];
|
|
131
|
+
declare function clearCollectedSpans(): void;
|
|
132
|
+
/**
|
|
133
|
+
* Create a logger.
|
|
134
|
+
*
|
|
135
|
+
* @param name - Logger namespace (e.g., 'myapp', 'myapp:db')
|
|
136
|
+
* @param config - Optional config array. Objects configure, arrays branch, values write.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* // Zero config (reads LOG_LEVEL, DEBUG, LOG_FORMAT from env)
|
|
140
|
+
* const log = createLogger('myapp')
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* // Configured pipeline
|
|
144
|
+
* const log = createLogger('myapp', [
|
|
145
|
+
* { level: 'debug', ns: '-sql' },
|
|
146
|
+
* console,
|
|
147
|
+
* { file: '/tmp/app.log', level: 'info', format: 'json' },
|
|
148
|
+
* ])
|
|
149
|
+
*/
|
|
150
|
+
declare function createLogger(name: string, config?: unknown[]): ConditionalLogger;
|
|
151
|
+
type LoggerFactory = (name: string, config?: unknown[]) => ConditionalLogger;
|
|
152
|
+
type LoggerPlugin = (factory: LoggerFactory) => LoggerFactory;
|
|
153
|
+
/**
|
|
154
|
+
* Compose a custom createLogger with plugins.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* import { createLogger as base, compose } from "loggily"
|
|
158
|
+
* import withSentry from "@sentry/loggily"
|
|
159
|
+
*
|
|
160
|
+
* const createLogger = compose(base, withSentry({ dsn: "..." }))
|
|
161
|
+
* const log = createLogger("myapp")
|
|
162
|
+
*/
|
|
163
|
+
declare function compose(base: LoggerFactory, ...plugins: LoggerPlugin[]): LoggerFactory;
|
|
164
|
+
/** @deprecated Use createLogger config array: createLogger("x", [{ level }, console]) */
|
|
165
|
+
declare function setLogLevel(level: LogLevel): void;
|
|
166
|
+
/** @deprecated Level is per-logger in v2 */
|
|
167
|
+
declare function getLogLevel(): LogLevel;
|
|
168
|
+
/** @deprecated Use TRACE=1 env var */
|
|
169
|
+
declare function enableSpans(): void;
|
|
170
|
+
/** @deprecated */
|
|
171
|
+
declare function disableSpans(): void;
|
|
172
|
+
/** @deprecated */
|
|
173
|
+
declare function spansAreEnabled(): boolean;
|
|
174
|
+
/** @deprecated Use TRACE=namespace env var */
|
|
175
|
+
declare function setTraceFilter(namespaces: string[] | null): void;
|
|
176
|
+
/** @deprecated */
|
|
177
|
+
declare function getTraceFilter(): string[] | null;
|
|
178
|
+
/** @deprecated Use DEBUG=namespace env var or { ns } in config array */
|
|
179
|
+
declare function setDebugFilter(namespaces: string[] | null): void;
|
|
180
|
+
/** @deprecated */
|
|
181
|
+
declare function getDebugFilter(): string[] | null;
|
|
182
|
+
/** @deprecated Use { format } in config array */
|
|
183
|
+
declare function setLogFormat(format: LogFormat): void;
|
|
184
|
+
/** @deprecated */
|
|
185
|
+
declare function getLogFormat(): LogFormat;
|
|
186
|
+
/** @deprecated Omit console from config array instead */
|
|
187
|
+
declare function setSuppressConsole(value: boolean): void;
|
|
188
|
+
type OutputMode = "console" | "stderr" | "writers-only";
|
|
189
|
+
/** @deprecated Use config array */
|
|
190
|
+
declare function setOutputMode(_mode: OutputMode): void;
|
|
191
|
+
/** @deprecated */
|
|
192
|
+
declare function getOutputMode(): OutputMode;
|
|
193
|
+
/** @deprecated Pass writers in config array instead */
|
|
194
|
+
declare function addWriter(writer: (formatted: string, level: string) => void): () => void;
|
|
195
|
+
/** @deprecated Spans dispatch through the pipeline automatically */
|
|
196
|
+
declare function writeSpan(namespace: string, duration: number, attrs: Record<string, unknown>): void;
|
|
197
|
+
//#endregion
|
|
198
|
+
export { setDebugFilter as A, Event as B, getCollectedSpans as C, getOutputMode as D, getLogLevel as E, setTraceFilter as F, OutputLogLevel as G, LogEvent as H, spansAreEnabled as I, Stage as J, Pipeline as K, startCollecting as L, setLogLevel as M, setOutputMode as N, getTraceFilter as O, setSuppressConsole as P, stopCollecting as R, enableSpans as S, getLogFormat as T, LogFormat as U, LOG_LEVEL_PRIORITY as V, LogLevel as W, defaultPipeline as X, buildPipeline as Y, safeStringify as Z, clearCollectedSpans as _, LoggerFactory as a, createSpanDataProxy as b, SpanData as c, SpanRecorder as d, _ambientRecorder as f, addWriter as g, _setContextHooks as h, Logger as i, setLogFormat as j, resetIds as k, SpanLogger as l, _setAmbientRecorder as m, LazyMessage as n, LoggerPlugin as o, _clearContextHooks as p, SpanEvent as q, LazyProps as r, OutputMode as s, ConditionalLogger as t, SpanRecord as u, compose as v, getDebugFilter as w, disableSpans as x, createLogger as y, writeSpan as z };
|
|
199
|
+
//# sourceMappingURL=core-DAFH-huv.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-DAFH-huv.d.mts","names":[],"sources":["../src/pipeline.ts","../src/core.ts"],"mappings":";KAKY,cAAA;AAAA,KACA,QAAA,GAAW,cAAA;AAAA,KACX,SAAA;AAAA,KAEA,QAAA;EACV,IAAA;EACA,IAAA;EACA,SAAA;EACA,KAAA,EAAO,cAAA;EACP,OAAA;EACA,KAAA,GAAQ,MAAA;AAAA;AAAA,KAGE,SAAA;EACV,IAAA;EACA,IAAA;EACA,SAAA;EACA,IAAA;EACA,QAAA;EACA,KAAA,GAAQ,MAAA;EACR,MAAA;EACA,OAAA;EACA,QAAA;AAAA;AAAA,KAGU,KAAA,GAAQ,QAAA,GAAW,SAAA;AAAA,KACnB,KAAA,IAAS,KAAA,EAAO,KAAA,KAAU,KAAA;AAAA,cAIzB,kBAAA,EAAoB,MAAA,CAAO,QAAA;AAAA,iBA2BxB,aAAA,CAAc,KAAA;AAAA,KA+ElB,QAAA,IAAY,SAAA;AAAA,UA8EP,QAAA;EACf,QAAA,GAAW,KAAA,EAAO,KAAA;EAClB,KAAA,EAAO,QAAA;EACP,OAAA;AAAA;AAAA,UAqCQ,WAAA;EACR,KAAA,EAAO,QAAA;EACP,EAAA,EAAI,QAAA;EACJ,MAAA,EAAQ,SAAA;AAAA;AAAA,iBAGM,aAAA,CAAc,QAAA,aAAqB,YAAA,GAAe,OAAA,CAAQ,WAAA,IAAe,QAAA;AAAA,iBA4HzE,eAAA,CAAA,GAAmB,QAAA;;;UC9VlB,UAAA;EAAA,SACN,IAAA;EAAA,SACA,UAAA;AAAA;AAAA,UAGM,YAAA;EACf,UAAA,CAAW,IAAA,EAAM,UAAA;AAAA;;YAIR,gBAAA,EAAkB,YAAA;AAAA,iBACb,mBAAA,CAAoB,QAAA,EAAU,YAAA;AAAA,KAMlC,WAAA;AAAA,KACA,SAAA,GAAY,MAAA,2BAAiC,MAAA;AAAA,UAExC,QAAA;EAAA,SACN,EAAA;EAAA,SACA,OAAA;EAAA,SACA,QAAA;EAAA,SACA,SAAA;EAAA,SACA,OAAA;EAAA,SACA,QAAA;EAAA,CACR,GAAA;AAAA;AAAA,UAGc,MAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA,EAAO,QAAA,CAAS,MAAA;EAAA,SAChB,QAAA,EAAU,QAAA;EAEnB,KAAA,CAAM,OAAA,EAAS,WAAA,EAAa,IAAA,GAAO,MAAA;EACnC,KAAA,CAAM,OAAA,EAAS,WAAA,EAAa,IAAA,GAAO,MAAA;EACnC,IAAA,CAAK,OAAA,EAAS,WAAA,EAAa,IAAA,GAAO,MAAA;EAClC,IAAA,CAAK,OAAA,EAAS,WAAA,EAAa,IAAA,GAAO,MAAA;EAClC,KAAA,CAAM,OAAA,EAAS,WAAA,EAAa,IAAA,GAAO,MAAA;EACnC,KAAA,CAAM,KAAA,EAAO,KAAA,EAAO,IAAA,GAAO,MAAA;EAC3B,KAAA,CAAM,KAAA,EAAO,KAAA,EAAO,OAAA,UAAiB,IAAA,GAAO,MAAA;EAE5C,MAAA,CAAO,SAAA,WAAoB,KAAA,GAAQ,MAAA,oBAA0B,iBAAA;EAC7D,IAAA,CAAK,SAAA,WAAoB,KAAA,GAAQ,SAAA,GAAY,UAAA;EAC7C,KAAA,CAAM,OAAA,EAAS,MAAA,oBAA0B,iBAAA;EDrDf;ECuD1B,KAAA,CAAM,OAAA,WAAkB,iBAAA;EACxB,GAAA;AAAA;AAAA,UAGe,UAAA,SAAmB,MAAA,EAAQ,UAAA;EAAA,SACjC,QAAA,EAAU,QAAA;IAAA,CAAc,GAAA;EAAA;AAAA;AAAA,UAKlB,iBAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA,EAAO,QAAA,CAAS,MAAA;EAAA,SAChB,QAAA,EAAU,QAAA;EAEnB,KAAA,IAAS,OAAA,EAAS,WAAA,EAAa,IAAA,GAAO,MAAA;EACtC,KAAA,IAAS,OAAA,EAAS,WAAA,EAAa,IAAA,GAAO,MAAA;EACtC,IAAA,IAAQ,OAAA,EAAS,WAAA,EAAa,IAAA,GAAO,MAAA;EACrC,IAAA,IAAQ,OAAA,EAAS,WAAA,EAAa,IAAA,GAAO,MAAA;EACrC,KAAA;IAAA,CACG,OAAA,EAAS,WAAA,EAAa,IAAA,GAAO,MAAA;IAAA,CAC7B,KAAA,EAAO,KAAA,EAAO,IAAA,GAAO,MAAA;IAAA,CACrB,KAAA,EAAO,KAAA,EAAO,OAAA,UAAiB,IAAA,GAAO,MAAA;EAAA;EAGzC,MAAA,CAAO,SAAA,WAAoB,KAAA,GAAQ,MAAA,oBAA0B,iBAAA;EAC7D,IAAA,CAAK,SAAA,WAAoB,KAAA,GAAQ,SAAA,GAAY,UAAA;EAC7C,KAAA,CAAM,OAAA,EAAS,MAAA,oBAA0B,iBAAA;EACzC,KAAA,CAAM,OAAA,WAAkB,iBAAA;EACxB,GAAA;AAAA;AAAA,iBAOc,QAAA,CAAA;;iBAYA,gBAAA,CAAiB,KAAA;EAC/B,cAAA,QAAsB,MAAA;EACtB,gBAAA;IAA0B,MAAA;IAAgB,OAAA;EAAA;EAC1C,YAAA,GAAe,MAAA,UAAgB,OAAA,UAAiB,QAAA;EAChD,WAAA,GAAc,MAAA;AAAA;;iBASA,kBAAA,CAAA;AAAA,UASN,cAAA;EACR,EAAA;EACA,OAAA;EACA,QAAA;EACA,SAAA;EACA,OAAA;EACA,QAAA;AAAA;AAAA,iBAGc,mBAAA,CAAoB,SAAA,QAAiB,cAAA,EAAgB,KAAA,EAAO,MAAA,oBAA0B,QAAA;AAAA,iBAwBtF,eAAA,CAAA;AAAA,iBAKA,cAAA,CAAA,GAAkB,QAAA;AAAA,iBAKlB,iBAAA,CAAA,GAAqB,QAAA;AAAA,iBAIrB,mBAAA,CAAA;;;;;;;;;;AD0LhB;;;;;;;;AC9VA;iBA6agB,YAAA,CAAa,IAAA,UAAc,MAAA,eAAqB,iBAAA;AAAA,KAQpD,aAAA,IAAiB,IAAA,UAAc,MAAA,iBAAuB,iBAAA;AAAA,KACtD,YAAA,IAAgB,OAAA,EAAS,aAAA,KAAkB,aAAA;;AAjbvD;;;;;;;;;iBA6bgB,OAAA,CAAQ,IAAA,EAAM,aAAA,KAAkB,OAAA,EAAS,YAAA,KAAiB,aAAA;;iBAa1D,WAAA,CAAY,KAAA,EAAO,QAAA;;iBAKnB,WAAA,CAAA,GAAe,QAAA;AAzc/B;AAAA,iBA8cgB,WAAA,CAAA;;iBAKA,YAAA,CAAA;;iBAKA,eAAA,CAAA;;iBAKA,cAAA,CAAe,UAAA;;iBASf,cAAA,CAAA;AA/dhB;AAAA,iBAoegB,cAAA,CAAe,UAAA;;iBASf,cAAA,CAAA;;iBAKA,YAAA,CAAa,MAAA,EAAQ,SAAA;;iBAKrB,YAAA,CAAA,GAAgB,SAAA;;iBAKhB,kBAAA,CAAmB,KAAA;AAAA,KAIvB,UAAA;;iBAGI,aAAA,CAAc,KAAA,EAAO,UAAA;;iBAGrB,aAAA,CAAA,GAAiB,UAAA;;iBAKjB,SAAA,CAAU,MAAA,GAAS,SAAA,UAAmB,KAAA;;iBAStC,SAAA,CAAU,SAAA,UAAmB,QAAA,UAAkB,KAAA,EAAO,MAAA"}
|