@better-logger/core 0.5.1 โ 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 +294 -294
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +36 -23
- package/dist/index.d.ts +36 -23
- package/dist/index.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,294 +1,294 @@
|
|
|
1
|
-
# @better-logger/core
|
|
2
|
-
|
|
3
|
-
> Track what your app *did*, not what it *said*.
|
|
4
|
-
|
|
5
|
-
Execution flow debugger for modern apps. Zero deps. Works everywhere. <10KB.
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @better-logger/core
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
Stop debugging like this:
|
|
14
|
-
|
|
15
|
-
```ts
|
|
16
|
-
console.log('start')
|
|
17
|
-
console.log('user', user)
|
|
18
|
-
console.log('after db')
|
|
19
|
-
console.log('done')
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
And trying to mentally reconstruct what happened, in what order, and how long each thing took.
|
|
23
|
-
|
|
24
|
-
Your app is not a list of messages.
|
|
25
|
-
|
|
26
|
-
It's a **flow**.
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## What you actually get
|
|
31
|
-
|
|
32
|
-
```
|
|
33
|
-
๐ [flow:user-onboarding] [auth] (tid: a1b2c3d4)
|
|
34
|
-
โ create-user
|
|
35
|
-
data: { email: "test@example.com" }
|
|
36
|
-
โ create-user (45ms)
|
|
37
|
-
โ generate-ai-profile
|
|
38
|
-
โ generate-ai-profile (410ms)
|
|
39
|
-
โ save-db
|
|
40
|
-
โ save-db (error: timeout)
|
|
41
|
-
๐ [flow:user-onboarding] failed (532ms)
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
```ts
|
|
45
|
-
import { createFlow } from '@better-logger/core'
|
|
46
|
-
|
|
47
|
-
const flow = createFlow('user-onboarding', { tags: ['auth'] })
|
|
48
|
-
|
|
49
|
-
const s1 = flow.step('create-user', { email })
|
|
50
|
-
await createUser(email)
|
|
51
|
-
s1.success()
|
|
52
|
-
|
|
53
|
-
const s2 = flow.step('save-db')
|
|
54
|
-
await saveUser()
|
|
55
|
-
s2.fail(new Error('timeout'))
|
|
56
|
-
|
|
57
|
-
flow.fail(new Error('save-db failed'))
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
You didn't write logs.
|
|
61
|
-
|
|
62
|
-
You defined the **execution**.
|
|
63
|
-
|
|
64
|
-
better-logger turns your code into a timeline of what actually happened.
|
|
65
|
-
|
|
66
|
-
Output is deferred until `flow.success()` / `flow.fail()` โ one atomic write, zero interleaving, even in async chaos.
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## Why this exists
|
|
71
|
-
|
|
72
|
-
Debugging today is broken:
|
|
73
|
-
|
|
74
|
-
- `console.log` โ flat noise, no structure
|
|
75
|
-
- No automatic timing
|
|
76
|
-
- No context propagation
|
|
77
|
-
- Async flows become impossible to follow
|
|
78
|
-
|
|
79
|
-
You don't debug code.
|
|
80
|
-
|
|
81
|
-
You debug **guesses about what happened**.
|
|
82
|
-
|
|
83
|
-
better-logger removes the guesswork. Instead of scattering `console.log` across your code and reconstructing timelines mentally, you define the flow upfront and get a structured narrative back โ with timing, context, and clear failure markers.
|
|
84
|
-
|
|
85
|
-
---
|
|
86
|
-
|
|
87
|
-
## What changes
|
|
88
|
-
|
|
89
|
-
Instead of guessing:
|
|
90
|
-
- "did this run?"
|
|
91
|
-
- "what ran first?"
|
|
92
|
-
- "where did it fail?"
|
|
93
|
-
- "why is it slow?"
|
|
94
|
-
|
|
95
|
-
You **see**:
|
|
96
|
-
|
|
97
|
-
- ๐ง A full execution narrative
|
|
98
|
-
- โฑ Exact timing for every step
|
|
99
|
-
- ๐ณ Nested flows (real hierarchy, not flat logs)
|
|
100
|
-
- ๐งพ Data at the moment it mattered
|
|
101
|
-
- ๐งต Shared context across the entire flow
|
|
102
|
-
- ๐ท Tags to group and filter
|
|
103
|
-
- ๐งฑ Guaranteed consistency โ no race-condition bugs
|
|
104
|
-
- ๐ Immutable render snapshots โ output can't be corrupted
|
|
105
|
-
|
|
106
|
-
This replaces `console.log` for anything non-trivial.
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
## Why not just use something else?
|
|
111
|
-
|
|
112
|
-
| Tool | Reality |
|
|
113
|
-
|------|--------|
|
|
114
|
-
| `console.log` | You reconstruct flows manually like it's 2005 |
|
|
115
|
-
| `pino` / `winston` | Built for shipping logs, not understanding execution |
|
|
116
|
-
| `debug` | Namespaces โ structure |
|
|
117
|
-
| OpenTelemetry | Powerful, but heavy and not built for everyday debugging |
|
|
118
|
-
|
|
119
|
-
**@better-logger/core** sits in the missing gap:
|
|
120
|
-
|
|
121
|
-
โ instant, local, zero-config execution tracing
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
## Usage
|
|
126
|
-
|
|
127
|
-
```ts
|
|
128
|
-
const flow = createFlow('request', { tags: ['api'] })
|
|
129
|
-
|
|
130
|
-
const step = flow.step('db')
|
|
131
|
-
await db.query()
|
|
132
|
-
step.success()
|
|
133
|
-
|
|
134
|
-
flow.success()
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
That's it.
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
## How it works
|
|
142
|
-
|
|
143
|
-
```mermaid
|
|
144
|
-
graph TD
|
|
145
|
-
Start[createFlow] --> Step[step]
|
|
146
|
-
Step --> Complete[success / fail]
|
|
147
|
-
Complete --> FlowEnd[flow.success / flow.fail]
|
|
148
|
-
FlowEnd --> Render[Atomic Render]
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
---
|
|
152
|
-
|
|
153
|
-
## When should you use this
|
|
154
|
-
|
|
155
|
-
Use it when:
|
|
156
|
-
|
|
157
|
-
- your flow has more than 2 steps
|
|
158
|
-
- async logic gets confusing
|
|
159
|
-
- debugging requires more than one `console.log`
|
|
160
|
-
- timing matters
|
|
161
|
-
- you need to know "what ran" and "what didn't"
|
|
162
|
-
|
|
163
|
-
If a single `console.log` feels enough, you don't need this.
|
|
164
|
-
|
|
165
|
-
If it doesn'tโฆ this replaces it.
|
|
166
|
-
|
|
167
|
-
---
|
|
168
|
-
|
|
169
|
-
## Advanced
|
|
170
|
-
|
|
171
|
-
### Child Flows
|
|
172
|
-
|
|
173
|
-
```ts
|
|
174
|
-
const flow = createFlow('request')
|
|
175
|
-
|
|
176
|
-
const ai = flow.child('ai-call')
|
|
177
|
-
const s = ai.step('generate')
|
|
178
|
-
await generate()
|
|
179
|
-
s.success()
|
|
180
|
-
ai.success()
|
|
181
|
-
|
|
182
|
-
flow.success()
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
```
|
|
186
|
-
๐ [flow:request] (tid: a1b2c3d4)
|
|
187
|
-
โโ ๐ [flow:ai-call] (tid: x9y8z7w6)
|
|
188
|
-
โ generate
|
|
189
|
-
โ generate (210ms)
|
|
190
|
-
๐ [flow:ai-call] success (215ms)
|
|
191
|
-
๐ [flow:request] success (340ms)
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### Context Propagation
|
|
195
|
-
|
|
196
|
-
```ts
|
|
197
|
-
flow.setContext({ userId: 'abc', requestId: 'xyz' })
|
|
198
|
-
// Available across all steps and child flows
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### Silent Mode
|
|
202
|
-
|
|
203
|
-
```ts
|
|
204
|
-
const flow = createFlow('task', { enabled: false })
|
|
205
|
-
// Near-zero overhead. All operations are no-ops.
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
### Max Steps Guard
|
|
209
|
-
|
|
210
|
-
```ts
|
|
211
|
-
const flow = createFlow('batch', { maxSteps: 1000 })
|
|
212
|
-
// Prevents memory blowups from runaway loops
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
### Subscribe (DevTools / Testing)
|
|
216
|
-
|
|
217
|
-
```ts
|
|
218
|
-
import { subscribe, toJSON } from '@better-logger/core'
|
|
219
|
-
|
|
220
|
-
subscribe((trace) => {
|
|
221
|
-
// Frozen snapshot of every completed flow
|
|
222
|
-
console.log(toJSON(trace))
|
|
223
|
-
})
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
### JSON Output
|
|
227
|
-
|
|
228
|
-
```ts
|
|
229
|
-
import { toJSON } from '@better-logger/core'
|
|
230
|
-
|
|
231
|
-
const json = toJSON(flow)
|
|
232
|
-
// { version: 1, flow: { id, name, state, status, tags, steps, context, ... } }
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
Portable, structured trace for tooling, testing, and future integrations.
|
|
236
|
-
|
|
237
|
-
---
|
|
238
|
-
|
|
239
|
-
## Features
|
|
240
|
-
|
|
241
|
-
### Core
|
|
242
|
-
`createFlow` ยท `step` ยท `success/fail` ยท `child` ยท `toJSON`
|
|
243
|
-
|
|
244
|
-
### Advanced
|
|
245
|
-
Context propagation ยท Tags ยท Inline data snapshots ยท Subscribe event tap ยท Deferred atomic rendering ยท Immutable snapshots
|
|
246
|
-
|
|
247
|
-
### Safety
|
|
248
|
-
Guarded finalization ยท `maxSteps` overflow protection ยท Lifecycle states (`running` โ `completed`) ยท Safe data serializer (circular refs, deep nesting) ยท NoOp singletons (zero-allocation silent mode)
|
|
249
|
-
|
|
250
|
-
---
|
|
251
|
-
|
|
252
|
-
## What's not included
|
|
253
|
-
|
|
254
|
-
- โ Log levels (debug, info, warn)
|
|
255
|
-
- โ Transports (file, HTTP, Elasticsearch)
|
|
256
|
-
- โ SaaS dashboards
|
|
257
|
-
- โ Auto-instrumentation
|
|
258
|
-
|
|
259
|
-
This is a developer debugging tool, not observability infrastructure.
|
|
260
|
-
|
|
261
|
-
---
|
|
262
|
-
|
|
263
|
-
## Roadmap
|
|
264
|
-
|
|
265
|
-
| Version | Focus |
|
|
266
|
-
|---------|-------|
|
|
267
|
-
| **V1** | Core flow debugger โ this release |
|
|
268
|
-
| **V2** | Custom renderers, `flow.run()` async helper, flow sampling |
|
|
269
|
-
| **V3** | Browser DevTools timeline panel |
|
|
270
|
-
| **V4+** | Python / Go / Rust ports, shared tracing schema |
|
|
271
|
-
|
|
272
|
-
---
|
|
273
|
-
|
|
274
|
-
## Organization
|
|
275
|
-
|
|
276
|
-
Published under the **@better-logger** org, owned by [0xmilord](https://github.com/0xmilord).
|
|
277
|
-
|
|
278
|
-
```bash
|
|
279
|
-
npm install @better-logger/core
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
All packages in this org share the same execution tracing schema.
|
|
283
|
-
|
|
284
|
-
---
|
|
285
|
-
|
|
286
|
-
## Test Coverage
|
|
287
|
-
|
|
288
|
-
**100%** across all metrics โ statements, branches, functions, lines.
|
|
289
|
-
|
|
290
|
-
[View detailed coverage report](./COVERAGE.md) | [Testing Strategy](./docs/TESTING-STRATEGY.md)
|
|
291
|
-
|
|
292
|
-
## License
|
|
293
|
-
|
|
294
|
-
MIT
|
|
1
|
+
# @better-logger/core
|
|
2
|
+
|
|
3
|
+
> Track what your app *did*, not what it *said*.
|
|
4
|
+
|
|
5
|
+
Execution flow debugger for modern apps. Zero deps. Works everywhere. <10KB.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @better-logger/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
Stop debugging like this:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
console.log('start')
|
|
17
|
+
console.log('user', user)
|
|
18
|
+
console.log('after db')
|
|
19
|
+
console.log('done')
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
And trying to mentally reconstruct what happened, in what order, and how long each thing took.
|
|
23
|
+
|
|
24
|
+
Your app is not a list of messages.
|
|
25
|
+
|
|
26
|
+
It's a **flow**.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## What you actually get
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
๐ [flow:user-onboarding] [auth] (tid: a1b2c3d4)
|
|
34
|
+
โ create-user
|
|
35
|
+
data: { email: "test@example.com" }
|
|
36
|
+
โ create-user (45ms)
|
|
37
|
+
โ generate-ai-profile
|
|
38
|
+
โ generate-ai-profile (410ms)
|
|
39
|
+
โ save-db
|
|
40
|
+
โ save-db (error: timeout)
|
|
41
|
+
๐ [flow:user-onboarding] failed (532ms)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { createFlow } from '@better-logger/core'
|
|
46
|
+
|
|
47
|
+
const flow = createFlow('user-onboarding', { tags: ['auth'] })
|
|
48
|
+
|
|
49
|
+
const s1 = flow.step('create-user', { email })
|
|
50
|
+
await createUser(email)
|
|
51
|
+
s1.success()
|
|
52
|
+
|
|
53
|
+
const s2 = flow.step('save-db')
|
|
54
|
+
await saveUser()
|
|
55
|
+
s2.fail(new Error('timeout'))
|
|
56
|
+
|
|
57
|
+
flow.fail(new Error('save-db failed'))
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
You didn't write logs.
|
|
61
|
+
|
|
62
|
+
You defined the **execution**.
|
|
63
|
+
|
|
64
|
+
better-logger turns your code into a timeline of what actually happened.
|
|
65
|
+
|
|
66
|
+
Output is deferred until `flow.success()` / `flow.fail()` โ one atomic write, zero interleaving, even in async chaos.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Why this exists
|
|
71
|
+
|
|
72
|
+
Debugging today is broken:
|
|
73
|
+
|
|
74
|
+
- `console.log` โ flat noise, no structure
|
|
75
|
+
- No automatic timing
|
|
76
|
+
- No context propagation
|
|
77
|
+
- Async flows become impossible to follow
|
|
78
|
+
|
|
79
|
+
You don't debug code.
|
|
80
|
+
|
|
81
|
+
You debug **guesses about what happened**.
|
|
82
|
+
|
|
83
|
+
better-logger removes the guesswork. Instead of scattering `console.log` across your code and reconstructing timelines mentally, you define the flow upfront and get a structured narrative back โ with timing, context, and clear failure markers.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## What changes
|
|
88
|
+
|
|
89
|
+
Instead of guessing:
|
|
90
|
+
- "did this run?"
|
|
91
|
+
- "what ran first?"
|
|
92
|
+
- "where did it fail?"
|
|
93
|
+
- "why is it slow?"
|
|
94
|
+
|
|
95
|
+
You **see**:
|
|
96
|
+
|
|
97
|
+
- ๐ง A full execution narrative
|
|
98
|
+
- โฑ Exact timing for every step
|
|
99
|
+
- ๐ณ Nested flows (real hierarchy, not flat logs)
|
|
100
|
+
- ๐งพ Data at the moment it mattered
|
|
101
|
+
- ๐งต Shared context across the entire flow
|
|
102
|
+
- ๐ท Tags to group and filter
|
|
103
|
+
- ๐งฑ Guaranteed consistency โ no race-condition bugs
|
|
104
|
+
- ๐ Immutable render snapshots โ output can't be corrupted
|
|
105
|
+
|
|
106
|
+
This replaces `console.log` for anything non-trivial.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Why not just use something else?
|
|
111
|
+
|
|
112
|
+
| Tool | Reality |
|
|
113
|
+
|------|--------|
|
|
114
|
+
| `console.log` | You reconstruct flows manually like it's 2005 |
|
|
115
|
+
| `pino` / `winston` | Built for shipping logs, not understanding execution |
|
|
116
|
+
| `debug` | Namespaces โ structure |
|
|
117
|
+
| OpenTelemetry | Powerful, but heavy and not built for everyday debugging |
|
|
118
|
+
|
|
119
|
+
**@better-logger/core** sits in the missing gap:
|
|
120
|
+
|
|
121
|
+
โ instant, local, zero-config execution tracing
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Usage
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
const flow = createFlow('request', { tags: ['api'] })
|
|
129
|
+
|
|
130
|
+
const step = flow.step('db')
|
|
131
|
+
await db.query()
|
|
132
|
+
step.success()
|
|
133
|
+
|
|
134
|
+
flow.success()
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
That's it.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## How it works
|
|
142
|
+
|
|
143
|
+
```mermaid
|
|
144
|
+
graph TD
|
|
145
|
+
Start[createFlow] --> Step[step]
|
|
146
|
+
Step --> Complete[success / fail]
|
|
147
|
+
Complete --> FlowEnd[flow.success / flow.fail]
|
|
148
|
+
FlowEnd --> Render[Atomic Render]
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## When should you use this
|
|
154
|
+
|
|
155
|
+
Use it when:
|
|
156
|
+
|
|
157
|
+
- your flow has more than 2 steps
|
|
158
|
+
- async logic gets confusing
|
|
159
|
+
- debugging requires more than one `console.log`
|
|
160
|
+
- timing matters
|
|
161
|
+
- you need to know "what ran" and "what didn't"
|
|
162
|
+
|
|
163
|
+
If a single `console.log` feels enough, you don't need this.
|
|
164
|
+
|
|
165
|
+
If it doesn'tโฆ this replaces it.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Advanced
|
|
170
|
+
|
|
171
|
+
### Child Flows
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
const flow = createFlow('request')
|
|
175
|
+
|
|
176
|
+
const ai = flow.child('ai-call')
|
|
177
|
+
const s = ai.step('generate')
|
|
178
|
+
await generate()
|
|
179
|
+
s.success()
|
|
180
|
+
ai.success()
|
|
181
|
+
|
|
182
|
+
flow.success()
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
๐ [flow:request] (tid: a1b2c3d4)
|
|
187
|
+
โโ ๐ [flow:ai-call] (tid: x9y8z7w6)
|
|
188
|
+
โ generate
|
|
189
|
+
โ generate (210ms)
|
|
190
|
+
๐ [flow:ai-call] success (215ms)
|
|
191
|
+
๐ [flow:request] success (340ms)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Context Propagation
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
flow.setContext({ userId: 'abc', requestId: 'xyz' })
|
|
198
|
+
// Available across all steps and child flows
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Silent Mode
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
const flow = createFlow('task', { enabled: false })
|
|
205
|
+
// Near-zero overhead. All operations are no-ops.
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Max Steps Guard
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
const flow = createFlow('batch', { maxSteps: 1000 })
|
|
212
|
+
// Prevents memory blowups from runaway loops
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Subscribe (DevTools / Testing)
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
import { subscribe, toJSON } from '@better-logger/core'
|
|
219
|
+
|
|
220
|
+
subscribe((trace) => {
|
|
221
|
+
// Frozen snapshot of every completed flow
|
|
222
|
+
console.log(toJSON(trace))
|
|
223
|
+
})
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### JSON Output
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
import { toJSON } from '@better-logger/core'
|
|
230
|
+
|
|
231
|
+
const json = toJSON(flow)
|
|
232
|
+
// { version: 1, flow: { id, name, state, status, tags, steps, context, ... } }
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Portable, structured trace for tooling, testing, and future integrations.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Features
|
|
240
|
+
|
|
241
|
+
### Core
|
|
242
|
+
`createFlow` ยท `step` ยท `success/fail` ยท `child` ยท `toJSON`
|
|
243
|
+
|
|
244
|
+
### Advanced
|
|
245
|
+
Context propagation ยท Tags ยท Inline data snapshots ยท Subscribe event tap ยท Deferred atomic rendering ยท Immutable snapshots
|
|
246
|
+
|
|
247
|
+
### Safety
|
|
248
|
+
Guarded finalization ยท `maxSteps` overflow protection ยท Lifecycle states (`running` โ `completed`) ยท Safe data serializer (circular refs, deep nesting) ยท NoOp singletons (zero-allocation silent mode)
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## What's not included
|
|
253
|
+
|
|
254
|
+
- โ Log levels (debug, info, warn)
|
|
255
|
+
- โ Transports (file, HTTP, Elasticsearch)
|
|
256
|
+
- โ SaaS dashboards
|
|
257
|
+
- โ Auto-instrumentation
|
|
258
|
+
|
|
259
|
+
This is a developer debugging tool, not observability infrastructure.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Roadmap
|
|
264
|
+
|
|
265
|
+
| Version | Focus |
|
|
266
|
+
|---------|-------|
|
|
267
|
+
| **V1** | Core flow debugger โ this release |
|
|
268
|
+
| **V2** | Custom renderers, `flow.run()` async helper, flow sampling |
|
|
269
|
+
| **V3** | Browser DevTools timeline panel |
|
|
270
|
+
| **V4+** | Python / Go / Rust ports, shared tracing schema |
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Organization
|
|
275
|
+
|
|
276
|
+
Published under the **@better-logger** org, owned by [0xmilord](https://github.com/0xmilord).
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
npm install @better-logger/core
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
All packages in this org share the same execution tracing schema.
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Test Coverage
|
|
287
|
+
|
|
288
|
+
**100%** across all metrics โ statements, branches, functions, lines.
|
|
289
|
+
|
|
290
|
+
[View detailed coverage report](./COVERAGE.md) | [Testing Strategy](./docs/TESTING-STRATEGY.md)
|
|
291
|
+
|
|
292
|
+
## License
|
|
293
|
+
|
|
294
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`))):(
|
|
3
|
-
`)))}};var
|
|
1
|
+
"use strict";var I=Object.defineProperty;var ne=Object.getOwnPropertyDescriptor;var oe=Object.getOwnPropertyNames;var ie=Object.prototype.hasOwnProperty;var se=(e,r)=>{for(var t in r)I(e,t,{get:r[t],enumerable:!0})},ae=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let i of oe(r))!ie.call(e,i)&&i!==t&&I(e,i,{get:()=>r[i],enumerable:!(n=ne(r,i))||n.enumerable});return e};var le=e=>ae(I({},"__esModule",{value:!0}),e);var ve={};se(ve,{NOOP_FLOW:()=>c,NOOP_STEP:()=>m,better:()=>d,createFlow:()=>k,getRenderer:()=>A,isEnabled:()=>x,normalizeError:()=>f,safeSerialize:()=>p,setClock:()=>j,setEnabled:()=>b,setIdGenerator:()=>Z,setRenderer:()=>G,subscribe:()=>H,toJSON:()=>J});module.exports=le(ve);function f(e){return e===void 0?{name:"Error",message:"Unknown error"}:e instanceof Error?{name:e.name,message:e.message,stack:e.stack}:typeof e=="string"?{name:"Error",message:e}:{name:"Error",message:String(e)}}var y=class{constructor(r,t){this._trace=r;this._clock=t;this._completed=!1}get isCompleted(){return this._completed}get trace(){return this._trace}success(){this._completed||(this._completed=!0,this._trace.end=this._clock.now(),this._trace.state="completed",this._trace.status="success")}fail(r){this._completed||(this._completed=!0,this._trace.end=this._clock.now(),this._trace.state="completed",this._trace.status="error",this._trace.error=f(r))}};var g=()=>{},m={success:g,fail:g},c={step:()=>m,child:()=>c,tag:g,tags:()=>[],setContext:g,getContext:()=>({}),success:g,fail:g,run:(e,r)=>{try{let t=r();return t instanceof Promise?t:Promise.resolve(t)}catch(t){return Promise.reject(t)}}};function U(e){let r=new WeakMap;function t(n){if(n===null||typeof n!="object")return n;if(n instanceof Date)return new Date(n.getTime());if(n instanceof RegExp)return new RegExp(n.source,n.flags);if(r.has(n))return r.get(n);if(Array.isArray(n)){let s=[];r.set(n,s);for(let o=0;o<n.length;o++)s.push(t(n[o]));return s}let i={};r.set(n,i);for(let s of Object.keys(n))i[s]=t(n[s]);return i}return t(e)}function R(e){if(e===null||typeof e!="object")return e;if(Object.freeze(e),Array.isArray(e))for(let r of e)R(r);else for(let r of Object.keys(e)){let t=e[r];t&&typeof t=="object"&&!Object.isFrozen(t)&&R(t)}return e}function z(e){let r=U(e);return R(r)}var O=new Set;function H(e){return O.add(e),()=>{O.delete(e)}}function ce(e){if(O.size!==0)for(let r of O)try{r(e)}catch{}}var C=class e{constructor(r,t,n,i,s){this._trace=r;this._clock=t;this._idGen=n;this._renderer=i;this._options=s;this._completed=!1;this._sequence=0}get isCompleted(){return this._completed}get trace(){return this._trace}step(r,t){if(this._completed)return m;let n=this._options.maxSteps;if(n!==void 0&&this._trace.steps.length>=n)return this._trace.meta===void 0?this._trace.meta={maxStepsExceeded:!0}:this._trace.meta.maxStepsExceeded=!0,m;let i={id:this._idGen.generate(),name:r,sequence:this._sequence++,state:"running",status:"pending",start:this._clock.now(),data:t,children:[]};return this._trace.steps.push(i),new y(i,this._clock)}child(r){if(this._completed)return c;if(this._options.enabled===!1)return c;let t={id:this._idGen.generate(),name:r,parentId:this._trace.id,state:"running",status:void 0,tags:[],start:this._clock.now(),context:{...this._trace.context},steps:[]};return new e(t,this._clock,this._idGen,this._renderer,{...this._options})}tag(r){this._completed||this._trace.tags.includes(r)||this._trace.tags.push(r)}tags(){return[...this._trace.tags]}setContext(r){this._completed||Object.assign(this._trace.context,r)}getContext(){return{...this._trace.context}}success(){this._finalize("success")}fail(r){this._trace.error=f(r),this._finalize("error")}run(r,t){if(this._completed)return Promise.resolve(t());let n=this.step(r);return Promise.resolve(t()).then(i=>(n.success(),i),i=>{throw n.fail(i),i})}_finalize(r){if(this._completed)return;this._completed=!0,this._trace.end=this._clock.now(),this._trace.state="completed",this._trace.status=r;let t=z(this._trace);ce(t),this._renderer.render(t)}};var h=class{generate(){return Math.random().toString(36).slice(2,10)}};var _=class{now(){return Date.now()}};function p(e,r){let t=r?.maxDepth??2,n=r?.maxLength??80,i=new WeakSet;function s(o,l){if(typeof o=="function")return`[Fn:${o.name||"?"}]`;if(typeof o=="symbol")return o.toString();if(l>t)return o&&typeof o=="object"?"[...]":o;if(o===null||typeof o!="object")return o;if(o instanceof Map)return`{Map(${o.size})}`;if(o instanceof Set)return`{Set(${o.size})}`;if(o instanceof Date)return o.toISOString();if(o instanceof RegExp)return o.toString();if(i.has(o))return"[Circular]";if(i.add(o),Array.isArray(o))return o.map(u=>s(u,l+1));let T={};for(let u of Object.keys(o)){let $=o[u];T[u]=s($,l+1)}return T}try{let o=JSON.stringify(s(e,0));return o&&o.length>n?o.slice(0,n)+"\u2026":o}catch{return"[Unserializable]"}}var de=()=>typeof process<"u"&&process.versions!==void 0&&process.versions.node!==void 0,ue=()=>typeof EdgeRuntime<"u",fe=()=>typeof caches<"u"&&"default"in caches,pe=()=>typeof window<"u"||typeof self<"u";function Y(){return de()?"node":ue()||fe()?"edge":pe()?"browser":"node"}var me="\x1B[0m",we="\x1B[32m",ge="\x1B[31m";var xe="\x1B[36m";var Fe="\x1B[2m";function he(){return!(Y()==="browser"||typeof process<"u"&&process.env?.NO_COLOR)}var _e=he();function v(e,r){return _e?`${r}${e}${me}`:e}function L(e){return v(e,we)}function E(e){return v(e,ge)}function K(e){return v(e,xe)}function P(e){return v(e,Fe)}function Q(e,r){if(r===void 0)return"\u2014";let t=Math.round(r-e);return t>=1e3?`${(t/1e3).toFixed(1)}s`:`${t}ms`}function V(e,r,t){let n=" ".repeat(r);t.push(`${n}${K("\u2192")} ${e.name}`),e.data!==void 0&&t.push(`${n} ${P("data:")} ${p(e.data,{maxLength:80})}`);let i=Q(e.start,e.end);e.status==="success"?t.push(`${n}${L("\u2713")} ${e.name} (${i})`):e.status==="error"?t.push(`${n}${E("\u2717")} ${e.name} (error: ${e.error?.message??"Unknown error"})`):t.push(`${n} ${e.name} (${i})`);for(let s of e.children)V(s,r+1,t)}var S=class{constructor(){this.name="console"}render(r){let t=[],n=r.tags.length>0?` [${r.tags.join(", ")}]`:"",i=r.status==="error"?"\u{1F525}":"\u{1F680}";t.push(`${i} [flow:${r.name}]${n} (${P("tid:")} ${r.id})`);for(let l of r.steps)V(l,1,t);let s=Q(r.start,r.end),o=r.meta?.maxStepsExceeded?` ${E("\u26A0\uFE0F maxSteps exceeded")}`:"";r.status==="error"?(t.push(`\u{1F3C1} [flow:${r.name}] ${E("failed")} (${s}, error: ${r.error?.message??"Unknown error"})${o}`),console.error(t.join(`
|
|
2
|
+
`))):(t.push(`\u{1F3C1} [flow:${r.name}] ${L("success")} (${s})${o}`),console.log(t.join(`
|
|
3
|
+
`)))}};var D=null;function G(e){D=e}function A(){return D}function X(e){return D??e}var B=new h,M=new _,Te=new S,W=!0;function b(e){W=e}function x(){return W}function Z(e){B=e}function j(e){M=e}function k(e,r){if(!W)return c;let t=r?.sample??1;if(t<1&&Math.random()>t)return c;if(r?.enabled===!1)return c;let n=X(Te),i={id:B.generate(),name:e,state:"running",tags:r?.tags??[],start:M.now(),context:r?.initialContext?{...r.initialContext}:{},steps:[]};return new C(i,M,B,n,r??{})}function ye(e){return e!==null&&typeof e=="object"&&("flow"in e||"tags"in e)}function Re(e,r,t){if(r.current&&t===void 0&&!r.current.completed)return r.current.flow;let n=t??e.flowName,i=k(n,{tags:[...e.defaultTags]});return r.current={flow:i,timer:null,lastError:null,completed:!1},i}function Oe(e,r){r.current&&(r.current.timer&&clearTimeout(r.current.timer),r.current.timer=setTimeout(()=>{ee(r)},e.idleTimeout))}function ee(e){!e.current||e.current.completed||(e.current.timer&&(clearTimeout(e.current.timer),e.current.timer=null),e.current.completed=!0,e.current.lastError?e.current.flow.fail(e.current.lastError):e.current.flow.success(),e.current=null)}function N(e,r,t,n,i,s){if(!x())return;let o,l;n===void 0?(o=void 0,l=void 0):ye(n)?i===void 0?(o=void 0,l=n):(o=void 0,l={...n,...i}):(o=n,l=i);let T=l?.flow,u=Re(e,r,T);if(l?.tags)for(let te of l.tags)u.tag(te);let $=s&&s!=="info"?`${t} [${s}]`:t,q=u.step($,o);s==="error"?(q.fail(o),r.current&&(r.current.lastError=o),u.tag("error")):(q.success(),s&&s!=="info"&&u.tag(s)),Oe(e,r)}var w={idleTimeout:100,flowName:"default",defaultTags:[]},F={current:null};function a(e,r,t){N(w,F,e,r,t)}a.info=(e,r,t)=>N(w,F,e,r,t,"info");a.warn=(e,r,t)=>N(w,F,e,r,t,"warn");a.error=(e,r,t)=>N(w,F,e,r,t,"error");a.setIdleTimeout=e=>{w.idleTimeout=e};a.setFlowName=e=>{w.flowName=e};a.setDefaultTags=e=>{w.defaultTags=e};a.activeFlow=()=>F.current?.flow??null;a.flush=()=>{ee(F)};var d=(e,r,t)=>{a(e,r,t)};d.log=a;d.flow=(e,r)=>k(e,r);d.setIdleTimeout=a.setIdleTimeout;d.setFlowName=a.setFlowName;d.setDefaultTags=a.setDefaultTags;d.activeFlow=a.activeFlow;d.flush=a.flush;d.setEnabled=b;d.isEnabled=x;function Ce(e){return typeof e=="object"&&e!==null&&"trace"in e&&typeof e.trace=="object"&&e.trace!==null}function J(e){let r=Ce(e)?e.trace:e;return JSON.stringify({version:1,flow:{id:r.id,name:r.name,...r.parentId!==void 0&&{parentId:r.parentId},state:r.state,...r.status!==void 0&&{status:r.status},tags:r.tags,start:r.start,...r.end!==void 0&&{end:r.end},...r.error!==void 0&&{error:r.error},context:r.context,steps:r.steps.map(re),...r.meta!==void 0&&{meta:r.meta}}},null,2)}function re(e){let r={id:e.id,name:e.name,sequence:e.sequence,state:e.state,status:e.status,start:e.start};if(e.end!==void 0&&(r.end=e.end),e.data!==void 0)try{r.data=JSON.parse(p(e.data))}catch{r.data="[Circular or unserializable]"}return e.error!==void 0&&(r.error=e.error),e.children.length>0&&(r.children=e.children.map(re)),r}0&&(module.exports={NOOP_FLOW,NOOP_STEP,better,createFlow,getRenderer,isEnabled,normalizeError,safeSerialize,setClock,setEnabled,setIdGenerator,setRenderer,subscribe,toJSON});
|
package/dist/index.d.cts
CHANGED
|
@@ -170,17 +170,16 @@ declare function setClock(clock: Clock): void;
|
|
|
170
170
|
declare function createFlow<Data = unknown>(name: string, options?: FlowOptions): FlowHandle<Data>;
|
|
171
171
|
|
|
172
172
|
/**
|
|
173
|
-
* V3: Zero-Friction
|
|
173
|
+
* V3: Zero-Friction better.log API
|
|
174
174
|
*
|
|
175
175
|
* Drop-in console.log replacement with auto-flow management.
|
|
176
176
|
*
|
|
177
177
|
* Usage:
|
|
178
|
-
* log('message')
|
|
179
|
-
* log('message', data)
|
|
180
|
-
* log('
|
|
181
|
-
* log('
|
|
182
|
-
*
|
|
183
|
-
* log.error('error', error)
|
|
178
|
+
* better.log('message')
|
|
179
|
+
* better.log('message', data)
|
|
180
|
+
* better.log.warn('warning', data)
|
|
181
|
+
* better.log.error('error', error)
|
|
182
|
+
* better.flow('explicit-flow', { tags: ['auth'] })
|
|
184
183
|
*/
|
|
185
184
|
|
|
186
185
|
/** Options for individual log calls */
|
|
@@ -190,22 +189,36 @@ interface LogCallOptions {
|
|
|
190
189
|
/** Additional tags for this step */
|
|
191
190
|
tags?: string[];
|
|
192
191
|
}
|
|
193
|
-
/**
|
|
194
|
-
interface
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
192
|
+
/** Options for creating an explicit flow via better.flow() */
|
|
193
|
+
interface BetterFlowOptions {
|
|
194
|
+
tags?: string[];
|
|
195
|
+
maxSteps?: number;
|
|
196
|
+
sample?: number;
|
|
197
|
+
initialContext?: Record<string, unknown>;
|
|
198
|
+
}
|
|
199
|
+
declare function logFn(message: string, dataOrOptions?: unknown, options?: LogCallOptions): void;
|
|
200
|
+
declare namespace logFn {
|
|
201
|
+
var info: (message: string, dataOrOptions?: unknown, options?: LogCallOptions) => void;
|
|
202
|
+
var warn: (message: string, dataOrOptions?: unknown, options?: LogCallOptions) => void;
|
|
203
|
+
var error: (message: string, dataOrOptions?: unknown, options?: LogCallOptions) => void;
|
|
204
|
+
var setIdleTimeout: (ms: number) => void;
|
|
205
|
+
var setFlowName: (name: string) => void;
|
|
206
|
+
var setDefaultTags: (tags: string[]) => void;
|
|
207
|
+
var activeFlow: () => FlowHandle | null;
|
|
208
|
+
var flush: () => void;
|
|
206
209
|
}
|
|
207
|
-
|
|
208
|
-
|
|
210
|
+
declare const betterBase: {
|
|
211
|
+
(message: string, dataOrOptions?: unknown, options?: LogCallOptions): void;
|
|
212
|
+
log: typeof logFn;
|
|
213
|
+
flow(name: string, options?: BetterFlowOptions): FlowHandle;
|
|
214
|
+
setIdleTimeout: (ms: number) => void;
|
|
215
|
+
setFlowName: (name: string) => void;
|
|
216
|
+
setDefaultTags: (tags: string[]) => void;
|
|
217
|
+
activeFlow: () => FlowHandle | null;
|
|
218
|
+
flush: () => void;
|
|
219
|
+
setEnabled: typeof setEnabled;
|
|
220
|
+
isEnabled: typeof isEnabled;
|
|
221
|
+
};
|
|
209
222
|
|
|
210
223
|
/** Contract for all renderer implementations */
|
|
211
224
|
interface FlowRenderer {
|
|
@@ -233,4 +246,4 @@ declare function toJSON(input: Readonly<FlowTrace> | {
|
|
|
233
246
|
trace: Readonly<FlowTrace>;
|
|
234
247
|
}): string;
|
|
235
248
|
|
|
236
|
-
export { type
|
|
249
|
+
export { type BetterFlowOptions, type Clock, type FlowListener, type FlowOptions, type FlowRenderer, type FlowState, type FlowStatus, type FlowTrace, type IdGenerator, type LogCallOptions, NOOP_FLOW, NOOP_STEP, type NoOpFlowHandle, type NoOpStepHandle, type NormalizedError, type SerializeOptions, type StepStatus, type StepTrace, betterBase as better, createFlow, getRenderer, isEnabled, normalizeError, safeSerialize, setClock, setEnabled, setIdGenerator, setRenderer, subscribe, toJSON };
|
package/dist/index.d.ts
CHANGED
|
@@ -170,17 +170,16 @@ declare function setClock(clock: Clock): void;
|
|
|
170
170
|
declare function createFlow<Data = unknown>(name: string, options?: FlowOptions): FlowHandle<Data>;
|
|
171
171
|
|
|
172
172
|
/**
|
|
173
|
-
* V3: Zero-Friction
|
|
173
|
+
* V3: Zero-Friction better.log API
|
|
174
174
|
*
|
|
175
175
|
* Drop-in console.log replacement with auto-flow management.
|
|
176
176
|
*
|
|
177
177
|
* Usage:
|
|
178
|
-
* log('message')
|
|
179
|
-
* log('message', data)
|
|
180
|
-
* log('
|
|
181
|
-
* log('
|
|
182
|
-
*
|
|
183
|
-
* log.error('error', error)
|
|
178
|
+
* better.log('message')
|
|
179
|
+
* better.log('message', data)
|
|
180
|
+
* better.log.warn('warning', data)
|
|
181
|
+
* better.log.error('error', error)
|
|
182
|
+
* better.flow('explicit-flow', { tags: ['auth'] })
|
|
184
183
|
*/
|
|
185
184
|
|
|
186
185
|
/** Options for individual log calls */
|
|
@@ -190,22 +189,36 @@ interface LogCallOptions {
|
|
|
190
189
|
/** Additional tags for this step */
|
|
191
190
|
tags?: string[];
|
|
192
191
|
}
|
|
193
|
-
/**
|
|
194
|
-
interface
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
192
|
+
/** Options for creating an explicit flow via better.flow() */
|
|
193
|
+
interface BetterFlowOptions {
|
|
194
|
+
tags?: string[];
|
|
195
|
+
maxSteps?: number;
|
|
196
|
+
sample?: number;
|
|
197
|
+
initialContext?: Record<string, unknown>;
|
|
198
|
+
}
|
|
199
|
+
declare function logFn(message: string, dataOrOptions?: unknown, options?: LogCallOptions): void;
|
|
200
|
+
declare namespace logFn {
|
|
201
|
+
var info: (message: string, dataOrOptions?: unknown, options?: LogCallOptions) => void;
|
|
202
|
+
var warn: (message: string, dataOrOptions?: unknown, options?: LogCallOptions) => void;
|
|
203
|
+
var error: (message: string, dataOrOptions?: unknown, options?: LogCallOptions) => void;
|
|
204
|
+
var setIdleTimeout: (ms: number) => void;
|
|
205
|
+
var setFlowName: (name: string) => void;
|
|
206
|
+
var setDefaultTags: (tags: string[]) => void;
|
|
207
|
+
var activeFlow: () => FlowHandle | null;
|
|
208
|
+
var flush: () => void;
|
|
206
209
|
}
|
|
207
|
-
|
|
208
|
-
|
|
210
|
+
declare const betterBase: {
|
|
211
|
+
(message: string, dataOrOptions?: unknown, options?: LogCallOptions): void;
|
|
212
|
+
log: typeof logFn;
|
|
213
|
+
flow(name: string, options?: BetterFlowOptions): FlowHandle;
|
|
214
|
+
setIdleTimeout: (ms: number) => void;
|
|
215
|
+
setFlowName: (name: string) => void;
|
|
216
|
+
setDefaultTags: (tags: string[]) => void;
|
|
217
|
+
activeFlow: () => FlowHandle | null;
|
|
218
|
+
flush: () => void;
|
|
219
|
+
setEnabled: typeof setEnabled;
|
|
220
|
+
isEnabled: typeof isEnabled;
|
|
221
|
+
};
|
|
209
222
|
|
|
210
223
|
/** Contract for all renderer implementations */
|
|
211
224
|
interface FlowRenderer {
|
|
@@ -233,4 +246,4 @@ declare function toJSON(input: Readonly<FlowTrace> | {
|
|
|
233
246
|
trace: Readonly<FlowTrace>;
|
|
234
247
|
}): string;
|
|
235
248
|
|
|
236
|
-
export { type
|
|
249
|
+
export { type BetterFlowOptions, type Clock, type FlowListener, type FlowOptions, type FlowRenderer, type FlowState, type FlowStatus, type FlowTrace, type IdGenerator, type LogCallOptions, NOOP_FLOW, NOOP_STEP, type NoOpFlowHandle, type NoOpStepHandle, type NormalizedError, type SerializeOptions, type StepStatus, type StepTrace, betterBase as better, createFlow, getRenderer, isEnabled, normalizeError, safeSerialize, setClock, setEnabled, setIdGenerator, setRenderer, subscribe, toJSON };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
function
|
|
2
|
-
`))):(
|
|
3
|
-
`)))}};var
|
|
1
|
+
function f(e){return e===void 0?{name:"Error",message:"Unknown error"}:e instanceof Error?{name:e.name,message:e.message,stack:e.stack}:typeof e=="string"?{name:"Error",message:e}:{name:"Error",message:String(e)}}var T=class{constructor(r,t){this._trace=r;this._clock=t;this._completed=!1}get isCompleted(){return this._completed}get trace(){return this._trace}success(){this._completed||(this._completed=!0,this._trace.end=this._clock.now(),this._trace.state="completed",this._trace.status="success")}fail(r){this._completed||(this._completed=!0,this._trace.end=this._clock.now(),this._trace.state="completed",this._trace.status="error",this._trace.error=f(r))}};var w=()=>{},g={success:w,fail:w},c={step:()=>g,child:()=>c,tag:w,tags:()=>[],setContext:w,getContext:()=>({}),success:w,fail:w,run:(e,r)=>{try{let t=r();return t instanceof Promise?t:Promise.resolve(t)}catch(t){return Promise.reject(t)}}};function B(e){let r=new WeakMap;function t(n){if(n===null||typeof n!="object")return n;if(n instanceof Date)return new Date(n.getTime());if(n instanceof RegExp)return new RegExp(n.source,n.flags);if(r.has(n))return r.get(n);if(Array.isArray(n)){let s=[];r.set(n,s);for(let o=0;o<n.length;o++)s.push(t(n[o]));return s}let i={};r.set(n,i);for(let s of Object.keys(n))i[s]=t(n[s]);return i}return t(e)}function y(e){if(e===null||typeof e!="object")return e;if(Object.freeze(e),Array.isArray(e))for(let r of e)y(r);else for(let r of Object.keys(e)){let t=e[r];t&&typeof t=="object"&&!Object.isFrozen(t)&&y(t)}return e}function $(e){let r=B(e);return y(r)}var R=new Set;function M(e){return R.add(e),()=>{R.delete(e)}}function ee(e){if(R.size!==0)for(let r of R)try{r(e)}catch{}}var O=class e{constructor(r,t,n,i,s){this._trace=r;this._clock=t;this._idGen=n;this._renderer=i;this._options=s;this._completed=!1;this._sequence=0}get isCompleted(){return this._completed}get trace(){return this._trace}step(r,t){if(this._completed)return g;let n=this._options.maxSteps;if(n!==void 0&&this._trace.steps.length>=n)return this._trace.meta===void 0?this._trace.meta={maxStepsExceeded:!0}:this._trace.meta.maxStepsExceeded=!0,g;let i={id:this._idGen.generate(),name:r,sequence:this._sequence++,state:"running",status:"pending",start:this._clock.now(),data:t,children:[]};return this._trace.steps.push(i),new T(i,this._clock)}child(r){if(this._completed)return c;if(this._options.enabled===!1)return c;let t={id:this._idGen.generate(),name:r,parentId:this._trace.id,state:"running",status:void 0,tags:[],start:this._clock.now(),context:{...this._trace.context},steps:[]};return new e(t,this._clock,this._idGen,this._renderer,{...this._options})}tag(r){this._completed||this._trace.tags.includes(r)||this._trace.tags.push(r)}tags(){return[...this._trace.tags]}setContext(r){this._completed||Object.assign(this._trace.context,r)}getContext(){return{...this._trace.context}}success(){this._finalize("success")}fail(r){this._trace.error=f(r),this._finalize("error")}run(r,t){if(this._completed)return Promise.resolve(t());let n=this.step(r);return Promise.resolve(t()).then(i=>(n.success(),i),i=>{throw n.fail(i),i})}_finalize(r){if(this._completed)return;this._completed=!0,this._trace.end=this._clock.now(),this._trace.state="completed",this._trace.status=r;let t=$(this._trace);ee(t),this._renderer.render(t)}};var F=class{generate(){return Math.random().toString(36).slice(2,10)}};var h=class{now(){return Date.now()}};function p(e,r){let t=r?.maxDepth??2,n=r?.maxLength??80,i=new WeakSet;function s(o,l){if(typeof o=="function")return`[Fn:${o.name||"?"}]`;if(typeof o=="symbol")return o.toString();if(l>t)return o&&typeof o=="object"?"[...]":o;if(o===null||typeof o!="object")return o;if(o instanceof Map)return`{Map(${o.size})}`;if(o instanceof Set)return`{Set(${o.size})}`;if(o instanceof Date)return o.toISOString();if(o instanceof RegExp)return o.toString();if(i.has(o))return"[Circular]";if(i.add(o),Array.isArray(o))return o.map(u=>s(u,l+1));let k={};for(let u of Object.keys(o)){let N=o[u];k[u]=s(N,l+1)}return k}try{let o=JSON.stringify(s(e,0));return o&&o.length>n?o.slice(0,n)+"\u2026":o}catch{return"[Unserializable]"}}var re=()=>typeof process<"u"&&process.versions!==void 0&&process.versions.node!==void 0,te=()=>typeof EdgeRuntime<"u",ne=()=>typeof caches<"u"&&"default"in caches,oe=()=>typeof window<"u"||typeof self<"u";function W(){return re()?"node":te()||ne()?"edge":oe()?"browser":"node"}var ie="\x1B[0m",se="\x1B[32m",ae="\x1B[31m";var le="\x1B[36m";var ce="\x1B[2m";function de(){return!(W()==="browser"||typeof process<"u"&&process.env?.NO_COLOR)}var ue=de();function C(e,r){return ue?`${r}${e}${ie}`:e}function I(e){return C(e,se)}function v(e){return C(e,ae)}function J(e){return C(e,le)}function z(e){return C(e,ce)}function q(e,r){if(r===void 0)return"\u2014";let t=Math.round(r-e);return t>=1e3?`${(t/1e3).toFixed(1)}s`:`${t}ms`}function U(e,r,t){let n=" ".repeat(r);t.push(`${n}${J("\u2192")} ${e.name}`),e.data!==void 0&&t.push(`${n} ${z("data:")} ${p(e.data,{maxLength:80})}`);let i=q(e.start,e.end);e.status==="success"?t.push(`${n}${I("\u2713")} ${e.name} (${i})`):e.status==="error"?t.push(`${n}${v("\u2717")} ${e.name} (error: ${e.error?.message??"Unknown error"})`):t.push(`${n} ${e.name} (${i})`);for(let s of e.children)U(s,r+1,t)}var _=class{constructor(){this.name="console"}render(r){let t=[],n=r.tags.length>0?` [${r.tags.join(", ")}]`:"",i=r.status==="error"?"\u{1F525}":"\u{1F680}";t.push(`${i} [flow:${r.name}]${n} (${z("tid:")} ${r.id})`);for(let l of r.steps)U(l,1,t);let s=q(r.start,r.end),o=r.meta?.maxStepsExceeded?` ${v("\u26A0\uFE0F maxSteps exceeded")}`:"";r.status==="error"?(t.push(`\u{1F3C1} [flow:${r.name}] ${v("failed")} (${s}, error: ${r.error?.message??"Unknown error"})${o}`),console.error(t.join(`
|
|
2
|
+
`))):(t.push(`\u{1F3C1} [flow:${r.name}] ${I("success")} (${s})${o}`),console.log(t.join(`
|
|
3
|
+
`)))}};var H=null;function Y(e){H=e}function K(){return H}function Q(e){return H??e}var L=new F,P=new h,me=new _,D=!0;function G(e){D=e}function S(){return D}function we(e){L=e}function ge(e){P=e}function E(e,r){if(!D)return c;let t=r?.sample??1;if(t<1&&Math.random()>t)return c;if(r?.enabled===!1)return c;let n=Q(me),i={id:L.generate(),name:e,state:"running",tags:r?.tags??[],start:P.now(),context:r?.initialContext?{...r.initialContext}:{},steps:[]};return new O(i,P,L,n,r??{})}function xe(e){return e!==null&&typeof e=="object"&&("flow"in e||"tags"in e)}function Fe(e,r,t){if(r.current&&t===void 0&&!r.current.completed)return r.current.flow;let n=t??e.flowName,i=E(n,{tags:[...e.defaultTags]});return r.current={flow:i,timer:null,lastError:null,completed:!1},i}function he(e,r){r.current&&(r.current.timer&&clearTimeout(r.current.timer),r.current.timer=setTimeout(()=>{V(r)},e.idleTimeout))}function V(e){!e.current||e.current.completed||(e.current.timer&&(clearTimeout(e.current.timer),e.current.timer=null),e.current.completed=!0,e.current.lastError?e.current.flow.fail(e.current.lastError):e.current.flow.success(),e.current=null)}function b(e,r,t,n,i,s){if(!S())return;let o,l;n===void 0?(o=void 0,l=void 0):xe(n)?i===void 0?(o=void 0,l=n):(o=void 0,l={...n,...i}):(o=n,l=i);let k=l?.flow,u=Fe(e,r,k);if(l?.tags)for(let j of l.tags)u.tag(j);let N=s&&s!=="info"?`${t} [${s}]`:t,A=u.step(N,o);s==="error"?(A.fail(o),r.current&&(r.current.lastError=o),u.tag("error")):(A.success(),s&&s!=="info"&&u.tag(s)),he(e,r)}var m={idleTimeout:100,flowName:"default",defaultTags:[]},x={current:null};function a(e,r,t){b(m,x,e,r,t)}a.info=(e,r,t)=>b(m,x,e,r,t,"info");a.warn=(e,r,t)=>b(m,x,e,r,t,"warn");a.error=(e,r,t)=>b(m,x,e,r,t,"error");a.setIdleTimeout=e=>{m.idleTimeout=e};a.setFlowName=e=>{m.flowName=e};a.setDefaultTags=e=>{m.defaultTags=e};a.activeFlow=()=>x.current?.flow??null;a.flush=()=>{V(x)};var d=(e,r,t)=>{a(e,r,t)};d.log=a;d.flow=(e,r)=>E(e,r);d.setIdleTimeout=a.setIdleTimeout;d.setFlowName=a.setFlowName;d.setDefaultTags=a.setDefaultTags;d.activeFlow=a.activeFlow;d.flush=a.flush;d.setEnabled=G;d.isEnabled=S;function _e(e){return typeof e=="object"&&e!==null&&"trace"in e&&typeof e.trace=="object"&&e.trace!==null}function X(e){let r=_e(e)?e.trace:e;return JSON.stringify({version:1,flow:{id:r.id,name:r.name,...r.parentId!==void 0&&{parentId:r.parentId},state:r.state,...r.status!==void 0&&{status:r.status},tags:r.tags,start:r.start,...r.end!==void 0&&{end:r.end},...r.error!==void 0&&{error:r.error},context:r.context,steps:r.steps.map(Z),...r.meta!==void 0&&{meta:r.meta}}},null,2)}function Z(e){let r={id:e.id,name:e.name,sequence:e.sequence,state:e.state,status:e.status,start:e.start};if(e.end!==void 0&&(r.end=e.end),e.data!==void 0)try{r.data=JSON.parse(p(e.data))}catch{r.data="[Circular or unserializable]"}return e.error!==void 0&&(r.error=e.error),e.children.length>0&&(r.children=e.children.map(Z)),r}export{c as NOOP_FLOW,g as NOOP_STEP,d as better,E as createFlow,K as getRenderer,S as isEnabled,f as normalizeError,p as safeSerialize,ge as setClock,G as setEnabled,we as setIdGenerator,Y as setRenderer,M as subscribe,X as toJSON};
|
package/package.json
CHANGED