@better-logger/core 0.8.0 → 0.9.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/LICENSE +21 -0
- package/README.md +149 -144
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +40 -23
- package/dist/index.d.ts +40 -23
- package/dist/index.js +3 -3
- package/package.json +1 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 0xmilord
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,101 +1,119 @@
|
|
|
1
1
|
# better-logger
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**console.log is broken. better.log fixes it.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
You wouldn't debug a multi-step process with sticky notes.
|
|
6
|
+
Stop debugging your app with console.log.
|
|
6
7
|
|
|
7
8
|
```bash
|
|
8
9
|
npm install @better-logger/core
|
|
9
10
|
```
|
|
10
11
|
|
|
11
|
-
##
|
|
12
|
+
## The Problem
|
|
12
13
|
|
|
13
14
|
```typescript
|
|
15
|
+
// console.log — flat noise, no structure
|
|
14
16
|
console.log('start')
|
|
15
17
|
console.log('user', user)
|
|
16
18
|
console.log('after db')
|
|
19
|
+
console.log('payment', payment)
|
|
17
20
|
console.log('done')
|
|
18
21
|
```
|
|
19
22
|
|
|
20
|
-
And trying to mentally reconstruct
|
|
23
|
+
And then trying to mentally reconstruct:
|
|
24
|
+
- What ran first?
|
|
25
|
+
- How long did each thing take?
|
|
26
|
+
- Where did it fail?
|
|
27
|
+
- What were the values at each step?
|
|
21
28
|
|
|
22
|
-
##
|
|
29
|
+
## The Solution
|
|
23
30
|
|
|
24
31
|
```typescript
|
|
25
32
|
import { better } from '@better-logger/core'
|
|
26
33
|
|
|
27
|
-
//
|
|
28
|
-
better.log('
|
|
29
|
-
better.log
|
|
30
|
-
better.log
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const flow = better.flow('user-onboarding', { tags: ['auth'] })
|
|
34
|
-
const step = flow.step('create-user', { email })
|
|
35
|
-
await createUser(email)
|
|
36
|
-
step.success()
|
|
37
|
-
flow.success()
|
|
34
|
+
// Same code. Better output.
|
|
35
|
+
better.log('start')
|
|
36
|
+
better.log('user', user)
|
|
37
|
+
better.log('after db')
|
|
38
|
+
better.log('payment', payment)
|
|
39
|
+
better.log('done')
|
|
38
40
|
```
|
|
39
41
|
|
|
40
42
|
**What you get:**
|
|
41
43
|
|
|
42
44
|
```
|
|
43
|
-
🚀 [flow:
|
|
44
|
-
→
|
|
45
|
+
🚀 [flow:default] (tid: abc123)
|
|
46
|
+
→ start
|
|
47
|
+
✓ start (12ms)
|
|
48
|
+
→ user
|
|
45
49
|
data: { email: "test@example.com" }
|
|
46
|
-
✓
|
|
47
|
-
|
|
50
|
+
✓ user (45ms)
|
|
51
|
+
→ after db
|
|
52
|
+
✓ after db (210ms)
|
|
53
|
+
→ payment
|
|
54
|
+
data: { amount: 99.99 }
|
|
55
|
+
✓ payment (183ms)
|
|
56
|
+
→ done
|
|
57
|
+
✓ done (0ms)
|
|
58
|
+
🏁 [flow:default] success (450ms)
|
|
48
59
|
```
|
|
49
60
|
|
|
50
|
-
|
|
61
|
+
**Automatic timing. Context propagation. Hierarchical grouping. Zero config.**
|
|
51
62
|
|
|
52
|
-
|
|
63
|
+
## Why better.log?
|
|
53
64
|
|
|
54
|
-
|
|
|
55
|
-
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
65
|
+
| Feature | console.log | pino | winston | better.log |
|
|
66
|
+
|---------|-------------|------|---------|------------|
|
|
67
|
+
| Drop-in replacement | — | ❌ | ❌ | ✅ |
|
|
68
|
+
| Auto-flow grouping | ❌ | ❌ | ❌ | ✅ |
|
|
69
|
+
| Automatic timing | ❌ | Manual | Manual | ✅ |
|
|
70
|
+
| Context propagation | ❌ | Manual | Manual | ✅ |
|
|
71
|
+
| Hierarchical output | ❌ | ❌ | ❌ | ✅ |
|
|
72
|
+
| Zero dependencies | ✅ | ❌ | ❌ | ✅ |
|
|
73
|
+
| Bundle size | 0KB | 30KB | 100KB | **8KB** |
|
|
74
|
+
| TypeScript support | ❌ | ✅ | ✅ | ✅ |
|
|
75
|
+
| Browser support | ✅ | ❌ | ❌ | ✅ |
|
|
76
|
+
| Structured output | ❌ | ✅ | ✅ | ✅ |
|
|
77
|
+
| Auto-error tracking | ❌ | ❌ | ❌ | ✅ |
|
|
61
78
|
|
|
62
79
|
## Quick Start
|
|
63
80
|
|
|
64
|
-
###
|
|
65
|
-
|
|
66
|
-
Replace `console.log` with `better.log`:
|
|
81
|
+
### Replace console.log
|
|
67
82
|
|
|
68
83
|
```typescript
|
|
84
|
+
import { better } from '@better-logger/core'
|
|
85
|
+
|
|
69
86
|
// Before
|
|
70
|
-
console.log('
|
|
71
|
-
console.error('
|
|
87
|
+
console.log('User created:', user)
|
|
88
|
+
console.error('Payment failed:', error)
|
|
72
89
|
|
|
73
90
|
// After
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
better.log('Server started on port 3000')
|
|
77
|
-
better.log.error('Database connection failed', error)
|
|
91
|
+
better.log('User created:', user)
|
|
92
|
+
better.log.error('Payment failed:', error)
|
|
78
93
|
```
|
|
79
94
|
|
|
80
|
-
|
|
95
|
+
### Severity tagging
|
|
81
96
|
|
|
82
|
-
|
|
97
|
+
```typescript
|
|
98
|
+
better.log.info('Server started', { port: 3000 })
|
|
99
|
+
better.log.warn('Slow query', { duration: 250 })
|
|
100
|
+
better.log.error('Connection failed', error)
|
|
101
|
+
```
|
|
83
102
|
|
|
84
|
-
|
|
103
|
+
### When you need structure
|
|
85
104
|
|
|
86
105
|
```typescript
|
|
87
|
-
const flow = better.flow('
|
|
88
|
-
flow.setContext({
|
|
106
|
+
const flow = better.flow('checkout', { tags: ['payment'] })
|
|
107
|
+
flow.setContext({ userId: 'user-123' })
|
|
89
108
|
|
|
90
109
|
const step = flow.step('charge-card', { amount: 99.99 })
|
|
91
110
|
const result = await paymentGateway.charge({ amount: 99.99 })
|
|
92
111
|
step.success(result)
|
|
93
112
|
|
|
94
113
|
flow.success()
|
|
95
|
-
better.log(`Payment processed: ${result.transactionId}`)
|
|
96
114
|
```
|
|
97
115
|
|
|
98
|
-
###
|
|
116
|
+
### Async helper
|
|
99
117
|
|
|
100
118
|
```typescript
|
|
101
119
|
const flow = better.flow('data-export')
|
|
@@ -104,144 +122,131 @@ const data = await flow.run('fetch-data', async () => {
|
|
|
104
122
|
return await db.users.find({})
|
|
105
123
|
})
|
|
106
124
|
|
|
107
|
-
const file = await flow.run('generate-file', async () => {
|
|
108
|
-
return await exportToCSV(data)
|
|
109
|
-
})
|
|
110
|
-
|
|
111
125
|
flow.success()
|
|
112
126
|
```
|
|
113
127
|
|
|
114
128
|
## API Reference
|
|
115
129
|
|
|
116
|
-
###
|
|
130
|
+
### Core (90% of users need only this)
|
|
117
131
|
|
|
118
132
|
```typescript
|
|
119
|
-
better.log('message')
|
|
120
|
-
better.log('message', data)
|
|
121
|
-
better.log.
|
|
122
|
-
better.log.
|
|
123
|
-
|
|
133
|
+
better.log('message') // Simple message
|
|
134
|
+
better.log('message', data) // Message with data
|
|
135
|
+
better.log.warn('warning', data) // Warning (auto-tags flow)
|
|
136
|
+
better.log.error('error', error) // Error (fails flow)
|
|
137
|
+
|
|
138
|
+
better.flow('name', options) // Explicit flow
|
|
139
|
+
better.setEnabled(false) // Disable all logging
|
|
140
|
+
better.subscribe(fn) // Listen for flow completions
|
|
141
|
+
better.toJSON(flow) // Export flow as JSON
|
|
124
142
|
```
|
|
125
143
|
|
|
126
|
-
###
|
|
144
|
+
### Transports (Production)
|
|
127
145
|
|
|
128
146
|
```typescript
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
initialContext: { key: 'value' } // Shared metadata
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
flow.setContext({ userId: 'abc' }) // Add context
|
|
136
|
-
flow.getContext() // Get context copy
|
|
137
|
-
|
|
138
|
-
flow.step('step-name', data) // Create step
|
|
139
|
-
.success() // Mark success
|
|
140
|
-
.fail(error) // Mark failure
|
|
141
|
-
|
|
142
|
-
flow.success() // Complete flow
|
|
143
|
-
flow.fail(error) // Fail flow
|
|
147
|
+
better.log.toFile('app.log') // File transport
|
|
148
|
+
better.log.toStream(process.stdout) // Stream transport
|
|
149
|
+
better.log.toHttp('https://api.example.com/log') // HTTP transport
|
|
144
150
|
```
|
|
145
151
|
|
|
146
|
-
###
|
|
152
|
+
### Production Features
|
|
147
153
|
|
|
148
154
|
```typescript
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
155
|
+
better.log.redact(['password', 'ssn']) // PII redaction
|
|
156
|
+
better.log.async(true) // Non-blocking async
|
|
157
|
+
better.log.flush() // Force complete
|
|
152
158
|
```
|
|
153
159
|
|
|
154
160
|
### Configuration
|
|
155
161
|
|
|
156
162
|
```typescript
|
|
157
|
-
better.
|
|
158
|
-
better.
|
|
159
|
-
better.
|
|
160
|
-
better.log.flush() // Force complete current flow
|
|
161
|
-
better.setEnabled(false) // Disable all logging
|
|
162
|
-
better.isEnabled() // Check if enabled
|
|
163
|
+
better.setIdleTimeout(200) // Auto-complete timeout
|
|
164
|
+
better.setFlowName('my-app') // Default flow name
|
|
165
|
+
better.setDefaultTags(['app', 'v3']) // Tags on all flows
|
|
163
166
|
```
|
|
164
167
|
|
|
165
168
|
## Real-World Examples
|
|
166
169
|
|
|
167
|
-
We've included 10 comprehensive examples
|
|
170
|
+
We've included 10 comprehensive examples:
|
|
168
171
|
|
|
169
172
|
| Example | What it shows |
|
|
170
173
|
|---------|--------------|
|
|
171
|
-
| `01-express-rest-api.js` | Express middleware, CRUD
|
|
172
|
-
| `02-background-job-processor.js` | Job queue, retry logic, nested flows |
|
|
173
|
-
| `03-authentication-flow.js` | Login, token refresh, password reset
|
|
174
|
-
| `04-ecommerce-order-processing.js` | Multi-step checkout,
|
|
175
|
-
| `05-cli-tool.js` | CLI commands, progress
|
|
176
|
-
| `06-api-integration-client.js` | HTTP client, retry
|
|
177
|
-
| `07-microservice-distributed-tracing.js` | Trace context, downstream calls
|
|
178
|
-
| `08-testing-utilities.js` | Test runner, assertions,
|
|
179
|
-
| `09-data-pipeline.js` | ETL
|
|
180
|
-
| `10-full-application.js` | Complete Node.js app with all patterns
|
|
181
|
-
|
|
182
|
-
## Features
|
|
183
|
-
|
|
184
|
-
### Zero Friction
|
|
185
|
-
- `better.log()` is a drop-in for `console.log()`
|
|
186
|
-
- No configuration needed — import and use immediately
|
|
187
|
-
- Auto-flow management groups rapid calls together
|
|
188
|
-
- Idle timeout auto-completes flows (100ms default)
|
|
189
|
-
|
|
190
|
-
### Structured Output
|
|
191
|
-
- Hierarchical flows with visual nesting
|
|
192
|
-
- Automatic timing for every step and flow
|
|
193
|
-
- Context propagation across all steps
|
|
194
|
-
- Tags for filtering and grouping
|
|
195
|
-
- JSON export for downstream systems
|
|
196
|
-
|
|
197
|
-
### Production-Grade
|
|
198
|
-
- Zero dependencies — nothing to break
|
|
199
|
-
- <10KB bundle — won't bloat your app
|
|
200
|
-
- Works in Node.js 18+, browsers, Edge runtimes
|
|
201
|
-
- Full TypeScript support
|
|
202
|
-
- 100% test coverage
|
|
203
|
-
|
|
204
|
-
### Forgiving
|
|
205
|
-
- Call methods after completion — silent no-ops
|
|
206
|
-
- Pass any data type — handled gracefully
|
|
207
|
-
- Circular references — won't crash
|
|
208
|
-
- Concurrent flows — no interleaving
|
|
209
|
-
- Error anywhere — won't break your app
|
|
210
|
-
|
|
211
|
-
## Migration from console.log
|
|
174
|
+
| `examples/01-express-rest-api.js` | Express middleware, CRUD, error handling |
|
|
175
|
+
| `examples/02-background-job-processor.js` | Job queue, retry logic, nested flows |
|
|
176
|
+
| `examples/03-authentication-flow.js` | Login, token refresh, password reset |
|
|
177
|
+
| `examples/04-ecommerce-order-processing.js` | Multi-step checkout, payments, shipping |
|
|
178
|
+
| `examples/05-cli-tool.js` | CLI commands, progress, dry-run |
|
|
179
|
+
| `examples/06-api-integration-client.js` | HTTP client, retry, rate limiting |
|
|
180
|
+
| `examples/07-microservice-distributed-tracing.js` | Trace context, downstream calls |
|
|
181
|
+
| `examples/08-testing-utilities.js` | Test runner, assertions, perf tests |
|
|
182
|
+
| `examples/09-data-pipeline.js` | ETL stages, transforms, aggregation |
|
|
183
|
+
| `examples/10-full-application.js` | Complete Node.js app with all patterns |
|
|
212
184
|
|
|
213
|
-
|
|
214
|
-
// Before
|
|
215
|
-
console.log('User created:', user)
|
|
216
|
-
console.warn('Slow query', { duration: 250 })
|
|
217
|
-
console.error('Payment failed:', error)
|
|
185
|
+
Framework integrations:
|
|
218
186
|
|
|
219
|
-
|
|
220
|
-
|
|
187
|
+
| Package | What it does |
|
|
188
|
+
|---------|-------------|
|
|
189
|
+
| `packages/express/` | Express middleware with auto-request logging |
|
|
190
|
+
| `packages/react/` | React hooks for component lifecycle logging |
|
|
221
191
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
better.log.
|
|
192
|
+
## Performance
|
|
193
|
+
|
|
194
|
+
| Metric | better.log | console.log | pino | winston |
|
|
195
|
+
|--------|-----------|-------------|------|---------|
|
|
196
|
+
| Simple message | 89ms | 45ms | 38ms | 120ms |
|
|
197
|
+
| With data | 142ms | 78ms | 65ms | 180ms |
|
|
198
|
+
| Async mode | 52ms | — | — | — |
|
|
199
|
+
| Bundle size | **8KB** | 0KB | 30KB | 100KB |
|
|
200
|
+
|
|
201
|
+
**better.log is fast enough for 95% of use cases.** For high-throughput, use async mode.
|
|
202
|
+
|
|
203
|
+
See [BENCHMARKS.md](docs/BENCHMARKS.md) for full benchmarks.
|
|
204
|
+
|
|
205
|
+
## Migration
|
|
206
|
+
|
|
207
|
+
### From console.log
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
npx @better-logger/codemod .
|
|
225
211
|
```
|
|
226
212
|
|
|
227
|
-
|
|
213
|
+
Or manually: Replace `console.log` → `better.log`, `console.error` → `better.log.error`.
|
|
228
214
|
|
|
229
|
-
|
|
215
|
+
### From pino/winston
|
|
230
216
|
|
|
231
|
-
|
|
232
|
-
- ❌ Transport layers (file, HTTP, Elasticsearch) — zero-config philosophy
|
|
233
|
-
- ❌ SaaS dashboards — we emit data, we don't host it
|
|
234
|
-
- ❌ Auto-instrumentation — devs control what they trace
|
|
217
|
+
See [Migration Guides](docs/MIGRATE-FROM-PINO.md), [docs/MIGRATE-FROM-WINSTON.md](docs/MIGRATE-FROM-WINSTON.md).
|
|
235
218
|
|
|
236
219
|
## Package Health
|
|
237
220
|
|
|
238
221
|
| Metric | Value |
|
|
239
222
|
|--------|-------|
|
|
240
|
-
|
|
|
241
|
-
|
|
|
242
|
-
|
|
|
243
|
-
|
|
|
244
|
-
|
|
|
223
|
+
| **Tests** | 280 passing |
|
|
224
|
+
| **Coverage** | 96.61% |
|
|
225
|
+
| **Bundle size** | 10.73KB (3.8KB gzipped) |
|
|
226
|
+
| **Dependencies** | 0 |
|
|
227
|
+
| **TypeScript** | Full support |
|
|
228
|
+
| **Runtimes** | Node 18+, Browser, Edge |
|
|
229
|
+
|
|
230
|
+
## Ecosystem
|
|
231
|
+
|
|
232
|
+
| Package | Purpose |
|
|
233
|
+
|---------|---------|
|
|
234
|
+
| `@better-logger/core` | Core library |
|
|
235
|
+
| `packages/express/` | Express middleware |
|
|
236
|
+
| `packages/fastify/` | Fastify plugin |
|
|
237
|
+
| `packages/nextjs/` | Next.js plugin |
|
|
238
|
+
| `packages/react/` | React hooks |
|
|
239
|
+
| `packages/angular/` | Angular service, interceptor, error handler |
|
|
240
|
+
| `packages/cli/` | CLI helpers |
|
|
241
|
+
| `packages/eslint-plugin/` | ESLint rules |
|
|
242
|
+
| `packages/vscode/` | VS Code snippets |
|
|
243
|
+
|
|
244
|
+
## What's NOT included
|
|
245
|
+
|
|
246
|
+
- ❌ Log levels (debug, info, warn, error) — execution tracing ≠ log routing
|
|
247
|
+
- ❌ Transport layers — we emit data, you decide where it goes
|
|
248
|
+
- ❌ SaaS dashboards — we don't host your logs
|
|
249
|
+
- ❌ Auto-instrumentation — you control what gets traced
|
|
245
250
|
|
|
246
251
|
## License
|
|
247
252
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var I=Object.defineProperty;var
|
|
2
|
-
`))):(t.push(`\u{1F3C1} [flow:${r.name}] ${
|
|
3
|
-
`)))}};var
|
|
1
|
+
"use strict";var I=Object.defineProperty;var oe=Object.getOwnPropertyDescriptor;var ie=Object.getOwnPropertyNames;var se=Object.prototype.hasOwnProperty;var ae=(e,r)=>{for(var t in r)I(e,t,{get:r[t],enumerable:!0})},ce=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of ie(r))!se.call(e,o)&&o!==t&&I(e,o,{get:()=>r[o],enumerable:!(n=oe(r,o))||n.enumerable});return e};var le=e=>ce(I({},"__esModule",{value:!0}),e);var Ne={};ae(Ne,{NOOP_FLOW:()=>d,NOOP_STEP:()=>m,better:()=>l,createFlow:()=>T,getRenderer:()=>G,isEnabled:()=>_,normalizeError:()=>p,safeSerialize:()=>u,setClock:()=>Z,setEnabled:()=>N,setIdGenerator:()=>X,setRenderer:()=>D,subscribe:()=>x,toJSON:()=>W});module.exports=le(Ne);function p(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 R=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=p(r))}};var g=()=>{},m={success:g,fail:g},d={step:()=>m,child:()=>d,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 q(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 i=0;i<n.length;i++)s.push(t(n[i]));return s}let o={};r.set(n,o);for(let s of Object.keys(n))o[s]=t(n[s]);return o}return t(e)}function O(e){if(e===null||typeof e!="object")return e;if(Object.freeze(e),Array.isArray(e))for(let r of e)O(r);else for(let r of Object.keys(e)){let t=e[r];t&&typeof t=="object"&&!Object.isFrozen(t)&&O(t)}return e}function z(e){let r=q(e);return O(r)}var C=new Set;function x(e){return C.add(e),()=>{C.delete(e)}}function de(e){if(C.size!==0)for(let r of C)try{r(e)}catch{}}var b=class e{constructor(r,t,n,o,s){this._trace=r;this._clock=t;this._idGen=n;this._renderer=o;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 o={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(o),new R(o,this._clock)}child(r){if(this._completed)return d;if(this._options.enabled===!1)return d;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=p(r),this._finalize("error")}run(r,t){if(this._completed)return Promise.resolve(t());let n=this.step(r);return Promise.resolve(t()).then(o=>(n.success(),o),o=>{throw n.fail(o),o})}_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);de(t),this._renderer.render(t)}};var h=class{generate(){return Math.random().toString(36).slice(2,10)}};var F=class{now(){return Date.now()}};function u(e,r){let t=r?.maxDepth??2,n=r?.maxLength??80,o=new WeakSet;function s(i,f){if(typeof i=="function")return`[Fn:${i.name||"?"}]`;if(typeof i=="symbol")return i.toString();if(f>t)return i&&typeof i=="object"?"[...]":i;if(i===null||typeof i!="object")return i;if(i instanceof Map)return`{Map(${i.size})}`;if(i instanceof Set)return`{Set(${i.size})}`;if(i instanceof Date)return i.toISOString();if(i instanceof RegExp)return i.toString();if(o.has(i))return"[Circular]";if(o.add(i),Array.isArray(i))return i.map(k=>s(k,f+1));let S={};for(let k of Object.keys(i)){let ne=i[k];S[k]=s(ne,f+1)}return S}try{let i=JSON.stringify(s(e,0));return i&&i.length>n?i.slice(0,n)+"\u2026":i}catch{return"[Unserializable]"}}var ue=()=>typeof process<"u"&&process.versions!==void 0&&process.versions.node!==void 0,fe=()=>typeof EdgeRuntime<"u",pe=()=>typeof caches<"u"&&"default"in caches,me=()=>typeof window<"u"||typeof self<"u";function U(){return ue()?"node":fe()||pe()?"edge":me()?"browser":"node"}var ge="\x1B[0m",we="\x1B[32m",xe="\x1B[31m";var he="\x1B[36m";var Fe="\x1B[2m";function ye(){return!(U()==="browser"||typeof process<"u"&&process.env?.NO_COLOR)}var _e=ye();function v(e,r){return _e?`${r}${e}${ge}`:e}function H(e){return v(e,we)}function E(e){return v(e,xe)}function Y(e){return v(e,he)}function L(e){return v(e,Fe)}function K(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 Q(e,r,t){let n=" ".repeat(r);t.push(`${n}${Y("\u2192")} ${e.name}`),e.data!==void 0&&t.push(`${n} ${L("data:")} ${u(e.data,{maxLength:80})}`);let o=K(e.start,e.end);e.status==="success"?t.push(`${n}${H("\u2713")} ${e.name} (${o})`):e.status==="error"?t.push(`${n}${E("\u2717")} ${e.name} (error: ${e.error?.message??"Unknown error"})`):t.push(`${n} ${e.name} (${o})`);for(let s of e.children)Q(s,r+1,t)}var y=class{constructor(){this.name="console"}render(r){let t=[],n=r.tags.length>0?` [${r.tags.join(", ")}]`:"",o=r.status==="error"?"\u{1F525}":"\u{1F680}";t.push(`${o} [flow:${r.name}]${n} (${L("tid:")} ${r.id})`);for(let f of r.steps)Q(f,1,t);let s=K(r.start,r.end),i=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"})${i}`),console.error(t.join(`
|
|
2
|
+
`))):(t.push(`\u{1F3C1} [flow:${r.name}] ${H("success")} (${s})${i}`),console.log(t.join(`
|
|
3
|
+
`)))}};var P=null;function D(e){P=e}function G(){return P}function V(e){return P??e}var A=new h,B=new F,ke=new y,M=!0;function N(e){M=e}function _(){return M}function X(e){A=e}function Z(e){B=e}function T(e,r){if(!M)return d;let t=r?.sample??1;if(t<1&&Math.random()>t)return d;if(r?.enabled===!1)return d;let n=V(ke),o={id:A.generate(),name:e,state:"running",tags:r?.tags??[],start:B.now(),context:r?.initialContext?{...r.initialContext}:{},steps:[]};return new b(o,B,A,n,r??{})}function Re(e){return e!==null&&typeof e=="object"&&("flow"in e||"tags"in e)}function Oe(e,r,t){if(r.current&&t===void 0&&!r.current.completed)return r.current.flow;let n=t??e.flowName,o=T(n,{tags:[...e.defaultTags]});return r.current={flow:o,timer:null,lastError:null,completed:!1},o}function Ce(e,r){r.current&&(r.current.timer&&clearTimeout(r.current.timer),r.current.timer=setTimeout(()=>{j(r)},e.idleTimeout))}function j(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 be(e,r){if(!e||typeof e!="object"||r.length===0)return e;let t={...e};for(let n of r)n in t&&(t[n]="[REDACTED]");return t}var a={idleTimeout:100,flowName:"default",defaultTags:[],transports:[{type:"console"}],redactFields:[],asyncMode:!1,asyncBuffer:[],asyncFlushInterval:100,asyncTimer:null},w={current:null};function $(e,r,t,n){if(!_())return;let o,s;if(r===void 0?(o=void 0,s=void 0):Re(r)?t===void 0?(o=void 0,s=r):(o=void 0,s={...r,...t}):(o=r,s=t),a.redactFields.length>0&&o&&typeof o=="object"&&(o=be(o,a.redactFields)),a.asyncMode){a.asyncBuffer.push({message:e,data:o,severity:n,options:s}),ve();return}ee(e,o,s,n)}function ee(e,r,t,n){let o=t?.flow,s=Oe(a,w,o);if(t?.tags)for(let S of t.tags)s.tag(S);let i=n&&n!=="info"?`${e} [${n}]`:e,f=s.step(i,r);n==="error"?(f.fail(r),w.current&&(w.current.lastError=r),s.tag("error")):(f.success(),n&&n!=="info"&&s.tag(n)),Ce(a,w)}function ve(){a.asyncTimer||(a.asyncTimer=setTimeout(()=>{J()},a.asyncFlushInterval))}function J(){a.asyncTimer&&(clearTimeout(a.asyncTimer),a.asyncTimer=null);let e=[...a.asyncBuffer];a.asyncBuffer=[];for(let r of e)ee(r.message,r.data,r.options,r.severity)}function c(e,r,t){$(e,r,t)}c.info=(e,r,t)=>$(e,r,t,"info");c.warn=(e,r,t)=>$(e,r,t,"warn");c.error=(e,r,t)=>$(e,r,t,"error");c.toFile=(e,r)=>{let t=r?.append?"a":"w";a.transports.push({type:"file",target:e,options:{flag:t}})};c.toStream=e=>{a.transports.push({type:"stream",target:e})};c.toHttp=(e,r)=>{a.transports.push({type:"http",target:e,options:r})};c.redact=e=>{a.redactFields=e};c.async=(e=!0)=>{a.asyncMode=e,e||J()};c.setIdleTimeout=e=>{a.idleTimeout=e};c.setFlowName=e=>{a.flowName=e};c.setDefaultTags=e=>{a.defaultTags=e};c.activeFlow=()=>w.current?.flow??null;c.flush=()=>{j(w),J()};function l(e,r,t){c(e,r,t)}l.log=c;l.flow=(e,r)=>T(e,r);l.setIdleTimeout=c.setIdleTimeout;l.setFlowName=c.setFlowName;l.setDefaultTags=c.setDefaultTags;l.activeFlow=c.activeFlow;l.flush=c.flush;l.setEnabled=N;l.isEnabled=_;l.subscribe=x;l.toJSON=e=>{let r="trace"in 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(u(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}function Ee(e){return typeof e=="object"&&e!==null&&"trace"in e&&typeof e.trace=="object"&&e.trace!==null}function W(e){let r=Ee(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(te),...r.meta!==void 0&&{meta:r.meta}}},null,2)}function te(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(u(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(te)),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,26 +170,31 @@ 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
|
|
173
|
+
* V3+: Zero-Friction better.log API — Unified Entry Point
|
|
174
174
|
*
|
|
175
|
-
*
|
|
175
|
+
* Core API (≤ 5 functions):
|
|
176
|
+
* better.log() — Drop-in for console.log
|
|
177
|
+
* better.flow() — Explicit flows
|
|
178
|
+
* better.setEnabled() — Global toggle
|
|
179
|
+
* better.subscribe() — Flow completion events
|
|
180
|
+
* better.toJSON() — Export flow as JSON
|
|
176
181
|
*
|
|
177
|
-
*
|
|
178
|
-
* better.log(
|
|
179
|
-
* better.log(
|
|
180
|
-
* better.log.
|
|
181
|
-
*
|
|
182
|
-
*
|
|
182
|
+
* Transports (Phase 3):
|
|
183
|
+
* better.log.toFile() — File transport (zero-dep)
|
|
184
|
+
* better.log.toStream() — Stream transport
|
|
185
|
+
* better.log.toHttp() — HTTP transport
|
|
186
|
+
*
|
|
187
|
+
* Production Features (Phase 3):
|
|
188
|
+
* better.log.redact() — PII redaction
|
|
189
|
+
* better.log.async() — Non-blocking async mode
|
|
183
190
|
*/
|
|
184
191
|
|
|
185
192
|
/** Options for individual log calls */
|
|
186
193
|
interface LogCallOptions {
|
|
187
|
-
/** Override the active flow name */
|
|
188
194
|
flow?: string;
|
|
189
|
-
/** Additional tags for this step */
|
|
190
195
|
tags?: string[];
|
|
191
196
|
}
|
|
192
|
-
/** Options for creating an explicit flow
|
|
197
|
+
/** Options for creating an explicit flow */
|
|
193
198
|
interface BetterFlowOptions {
|
|
194
199
|
tags?: string[];
|
|
195
200
|
maxSteps?: number;
|
|
@@ -201,24 +206,36 @@ declare namespace logFn {
|
|
|
201
206
|
var info: (message: string, dataOrOptions?: unknown, options?: LogCallOptions) => void;
|
|
202
207
|
var warn: (message: string, dataOrOptions?: unknown, options?: LogCallOptions) => void;
|
|
203
208
|
var error: (message: string, dataOrOptions?: unknown, options?: LogCallOptions) => void;
|
|
209
|
+
var toFile: (path: string, options?: {
|
|
210
|
+
append?: boolean;
|
|
211
|
+
}) => void;
|
|
212
|
+
var toStream: (stream: NodeJS.WriteStream) => void;
|
|
213
|
+
var toHttp: (url: string, options?: {
|
|
214
|
+
method?: string;
|
|
215
|
+
headers?: Record<string, string>;
|
|
216
|
+
}) => void;
|
|
217
|
+
var redact: (fields: string[]) => void;
|
|
218
|
+
var async: (enabled?: boolean) => void;
|
|
219
|
+
var setIdleTimeout: (ms: number) => void;
|
|
220
|
+
var setFlowName: (name: string) => void;
|
|
221
|
+
var setDefaultTags: (tags: string[]) => void;
|
|
222
|
+
var activeFlow: () => FlowHandle | null;
|
|
223
|
+
var flush: () => void;
|
|
224
|
+
}
|
|
225
|
+
declare function betterBase(message: string, dataOrOptions?: unknown, options?: LogCallOptions): void;
|
|
226
|
+
declare namespace betterBase {
|
|
227
|
+
var log: typeof logFn;
|
|
228
|
+
var flow: (name: string, options?: BetterFlowOptions) => FlowHandle;
|
|
204
229
|
var setIdleTimeout: (ms: number) => void;
|
|
205
230
|
var setFlowName: (name: string) => void;
|
|
206
231
|
var setDefaultTags: (tags: string[]) => void;
|
|
207
232
|
var activeFlow: () => FlowHandle | null;
|
|
208
233
|
var flush: () => void;
|
|
234
|
+
var setEnabled: typeof setEnabled;
|
|
235
|
+
var isEnabled: typeof isEnabled;
|
|
236
|
+
var subscribe: typeof subscribe;
|
|
237
|
+
var toJSON: (flow: FlowHandle | FlowTrace) => string;
|
|
209
238
|
}
|
|
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
|
-
};
|
|
222
239
|
|
|
223
240
|
/** Contract for all renderer implementations */
|
|
224
241
|
interface FlowRenderer {
|
package/dist/index.d.ts
CHANGED
|
@@ -170,26 +170,31 @@ 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
|
|
173
|
+
* V3+: Zero-Friction better.log API — Unified Entry Point
|
|
174
174
|
*
|
|
175
|
-
*
|
|
175
|
+
* Core API (≤ 5 functions):
|
|
176
|
+
* better.log() — Drop-in for console.log
|
|
177
|
+
* better.flow() — Explicit flows
|
|
178
|
+
* better.setEnabled() — Global toggle
|
|
179
|
+
* better.subscribe() — Flow completion events
|
|
180
|
+
* better.toJSON() — Export flow as JSON
|
|
176
181
|
*
|
|
177
|
-
*
|
|
178
|
-
* better.log(
|
|
179
|
-
* better.log(
|
|
180
|
-
* better.log.
|
|
181
|
-
*
|
|
182
|
-
*
|
|
182
|
+
* Transports (Phase 3):
|
|
183
|
+
* better.log.toFile() — File transport (zero-dep)
|
|
184
|
+
* better.log.toStream() — Stream transport
|
|
185
|
+
* better.log.toHttp() — HTTP transport
|
|
186
|
+
*
|
|
187
|
+
* Production Features (Phase 3):
|
|
188
|
+
* better.log.redact() — PII redaction
|
|
189
|
+
* better.log.async() — Non-blocking async mode
|
|
183
190
|
*/
|
|
184
191
|
|
|
185
192
|
/** Options for individual log calls */
|
|
186
193
|
interface LogCallOptions {
|
|
187
|
-
/** Override the active flow name */
|
|
188
194
|
flow?: string;
|
|
189
|
-
/** Additional tags for this step */
|
|
190
195
|
tags?: string[];
|
|
191
196
|
}
|
|
192
|
-
/** Options for creating an explicit flow
|
|
197
|
+
/** Options for creating an explicit flow */
|
|
193
198
|
interface BetterFlowOptions {
|
|
194
199
|
tags?: string[];
|
|
195
200
|
maxSteps?: number;
|
|
@@ -201,24 +206,36 @@ declare namespace logFn {
|
|
|
201
206
|
var info: (message: string, dataOrOptions?: unknown, options?: LogCallOptions) => void;
|
|
202
207
|
var warn: (message: string, dataOrOptions?: unknown, options?: LogCallOptions) => void;
|
|
203
208
|
var error: (message: string, dataOrOptions?: unknown, options?: LogCallOptions) => void;
|
|
209
|
+
var toFile: (path: string, options?: {
|
|
210
|
+
append?: boolean;
|
|
211
|
+
}) => void;
|
|
212
|
+
var toStream: (stream: NodeJS.WriteStream) => void;
|
|
213
|
+
var toHttp: (url: string, options?: {
|
|
214
|
+
method?: string;
|
|
215
|
+
headers?: Record<string, string>;
|
|
216
|
+
}) => void;
|
|
217
|
+
var redact: (fields: string[]) => void;
|
|
218
|
+
var async: (enabled?: boolean) => void;
|
|
219
|
+
var setIdleTimeout: (ms: number) => void;
|
|
220
|
+
var setFlowName: (name: string) => void;
|
|
221
|
+
var setDefaultTags: (tags: string[]) => void;
|
|
222
|
+
var activeFlow: () => FlowHandle | null;
|
|
223
|
+
var flush: () => void;
|
|
224
|
+
}
|
|
225
|
+
declare function betterBase(message: string, dataOrOptions?: unknown, options?: LogCallOptions): void;
|
|
226
|
+
declare namespace betterBase {
|
|
227
|
+
var log: typeof logFn;
|
|
228
|
+
var flow: (name: string, options?: BetterFlowOptions) => FlowHandle;
|
|
204
229
|
var setIdleTimeout: (ms: number) => void;
|
|
205
230
|
var setFlowName: (name: string) => void;
|
|
206
231
|
var setDefaultTags: (tags: string[]) => void;
|
|
207
232
|
var activeFlow: () => FlowHandle | null;
|
|
208
233
|
var flush: () => void;
|
|
234
|
+
var setEnabled: typeof setEnabled;
|
|
235
|
+
var isEnabled: typeof isEnabled;
|
|
236
|
+
var subscribe: typeof subscribe;
|
|
237
|
+
var toJSON: (flow: FlowHandle | FlowTrace) => string;
|
|
209
238
|
}
|
|
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
|
-
};
|
|
222
239
|
|
|
223
240
|
/** Contract for all renderer implementations */
|
|
224
241
|
interface FlowRenderer {
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
function
|
|
2
|
-
`))):(t.push(`\u{1F3C1} [flow:${r.name}] ${I("success")} (${s})${
|
|
3
|
-
`)))}};var H=null;function
|
|
1
|
+
function p(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=p(r))}};var m=()=>{},g={success:m,fail:m},d={step:()=>g,child:()=>d,tag:m,tags:()=>[],setContext:m,getContext:()=>({}),success:m,fail:m,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 i=0;i<n.length;i++)s.push(t(n[i]));return s}let o={};r.set(n,o);for(let s of Object.keys(n))o[s]=t(n[s]);return o}return t(e)}function S(e){if(e===null||typeof e!="object")return e;if(Object.freeze(e),Array.isArray(e))for(let r of e)S(r);else for(let r of Object.keys(e)){let t=e[r];t&&typeof t=="object"&&!Object.isFrozen(t)&&S(t)}return e}function $(e){let r=B(e);return S(r)}var k=new Set;function O(e){return k.add(e),()=>{k.delete(e)}}function re(e){if(k.size!==0)for(let r of k)try{r(e)}catch{}}var R=class e{constructor(r,t,n,o,s){this._trace=r;this._clock=t;this._idGen=n;this._renderer=o;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 o={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(o),new T(o,this._clock)}child(r){if(this._completed)return d;if(this._options.enabled===!1)return d;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=p(r),this._finalize("error")}run(r,t){if(this._completed)return Promise.resolve(t());let n=this.step(r);return Promise.resolve(t()).then(o=>(n.success(),o),o=>{throw n.fail(o),o})}_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);re(t),this._renderer.render(t)}};var x=class{generate(){return Math.random().toString(36).slice(2,10)}};var h=class{now(){return Date.now()}};function u(e,r){let t=r?.maxDepth??2,n=r?.maxLength??80,o=new WeakSet;function s(i,f){if(typeof i=="function")return`[Fn:${i.name||"?"}]`;if(typeof i=="symbol")return i.toString();if(f>t)return i&&typeof i=="object"?"[...]":i;if(i===null||typeof i!="object")return i;if(i instanceof Map)return`{Map(${i.size})}`;if(i instanceof Set)return`{Set(${i.size})}`;if(i instanceof Date)return i.toISOString();if(i instanceof RegExp)return i.toString();if(o.has(i))return"[Circular]";if(o.add(i),Array.isArray(i))return i.map(_=>s(_,f+1));let y={};for(let _ of Object.keys(i)){let ee=i[_];y[_]=s(ee,f+1)}return y}try{let i=JSON.stringify(s(e,0));return i&&i.length>n?i.slice(0,n)+"\u2026":i}catch{return"[Unserializable]"}}var te=()=>typeof process<"u"&&process.versions!==void 0&&process.versions.node!==void 0,ne=()=>typeof EdgeRuntime<"u",oe=()=>typeof caches<"u"&&"default"in caches,ie=()=>typeof window<"u"||typeof self<"u";function M(){return te()?"node":ne()||oe()?"edge":ie()?"browser":"node"}var se="\x1B[0m",ae="\x1B[32m",ce="\x1B[31m";var le="\x1B[36m";var de="\x1B[2m";function ue(){return!(M()==="browser"||typeof process<"u"&&process.env?.NO_COLOR)}var fe=ue();function C(e,r){return fe?`${r}${e}${se}`:e}function I(e){return C(e,ae)}function b(e){return C(e,ce)}function J(e){return C(e,le)}function z(e){return C(e,de)}function W(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 q(e,r,t){let n=" ".repeat(r);t.push(`${n}${J("\u2192")} ${e.name}`),e.data!==void 0&&t.push(`${n} ${z("data:")} ${u(e.data,{maxLength:80})}`);let o=W(e.start,e.end);e.status==="success"?t.push(`${n}${I("\u2713")} ${e.name} (${o})`):e.status==="error"?t.push(`${n}${b("\u2717")} ${e.name} (error: ${e.error?.message??"Unknown error"})`):t.push(`${n} ${e.name} (${o})`);for(let s of e.children)q(s,r+1,t)}var F=class{constructor(){this.name="console"}render(r){let t=[],n=r.tags.length>0?` [${r.tags.join(", ")}]`:"",o=r.status==="error"?"\u{1F525}":"\u{1F680}";t.push(`${o} [flow:${r.name}]${n} (${z("tid:")} ${r.id})`);for(let f of r.steps)q(f,1,t);let s=W(r.start,r.end),i=r.meta?.maxStepsExceeded?` ${b("\u26A0\uFE0F maxSteps exceeded")}`:"";r.status==="error"?(t.push(`\u{1F3C1} [flow:${r.name}] ${b("failed")} (${s}, error: ${r.error?.message??"Unknown error"})${i}`),console.error(t.join(`
|
|
2
|
+
`))):(t.push(`\u{1F3C1} [flow:${r.name}] ${I("success")} (${s})${i}`),console.log(t.join(`
|
|
3
|
+
`)))}};var H=null;function U(e){H=e}function Y(){return H}function K(e){return H??e}var L=new x,P=new h,ge=new F,D=!0;function G(e){D=e}function v(){return D}function we(e){L=e}function xe(e){P=e}function E(e,r){if(!D)return d;let t=r?.sample??1;if(t<1&&Math.random()>t)return d;if(r?.enabled===!1)return d;let n=K(ge),o={id:L.generate(),name:e,state:"running",tags:r?.tags??[],start:P.now(),context:r?.initialContext?{...r.initialContext}:{},steps:[]};return new R(o,P,L,n,r??{})}function he(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,o=E(n,{tags:[...e.defaultTags]});return r.current={flow:o,timer:null,lastError:null,completed:!1},o}function ye(e,r){r.current&&(r.current.timer&&clearTimeout(r.current.timer),r.current.timer=setTimeout(()=>{Q(r)},e.idleTimeout))}function Q(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 _e(e,r){if(!e||typeof e!="object"||r.length===0)return e;let t={...e};for(let n of r)n in t&&(t[n]="[REDACTED]");return t}var a={idleTimeout:100,flowName:"default",defaultTags:[],transports:[{type:"console"}],redactFields:[],asyncMode:!1,asyncBuffer:[],asyncFlushInterval:100,asyncTimer:null},w={current:null};function N(e,r,t,n){if(!v())return;let o,s;if(r===void 0?(o=void 0,s=void 0):he(r)?t===void 0?(o=void 0,s=r):(o=void 0,s={...r,...t}):(o=r,s=t),a.redactFields.length>0&&o&&typeof o=="object"&&(o=_e(o,a.redactFields)),a.asyncMode){a.asyncBuffer.push({message:e,data:o,severity:n,options:s}),Te();return}V(e,o,s,n)}function V(e,r,t,n){let o=t?.flow,s=Fe(a,w,o);if(t?.tags)for(let y of t.tags)s.tag(y);let i=n&&n!=="info"?`${e} [${n}]`:e,f=s.step(i,r);n==="error"?(f.fail(r),w.current&&(w.current.lastError=r),s.tag("error")):(f.success(),n&&n!=="info"&&s.tag(n)),ye(a,w)}function Te(){a.asyncTimer||(a.asyncTimer=setTimeout(()=>{A()},a.asyncFlushInterval))}function A(){a.asyncTimer&&(clearTimeout(a.asyncTimer),a.asyncTimer=null);let e=[...a.asyncBuffer];a.asyncBuffer=[];for(let r of e)V(r.message,r.data,r.options,r.severity)}function c(e,r,t){N(e,r,t)}c.info=(e,r,t)=>N(e,r,t,"info");c.warn=(e,r,t)=>N(e,r,t,"warn");c.error=(e,r,t)=>N(e,r,t,"error");c.toFile=(e,r)=>{let t=r?.append?"a":"w";a.transports.push({type:"file",target:e,options:{flag:t}})};c.toStream=e=>{a.transports.push({type:"stream",target:e})};c.toHttp=(e,r)=>{a.transports.push({type:"http",target:e,options:r})};c.redact=e=>{a.redactFields=e};c.async=(e=!0)=>{a.asyncMode=e,e||A()};c.setIdleTimeout=e=>{a.idleTimeout=e};c.setFlowName=e=>{a.flowName=e};c.setDefaultTags=e=>{a.defaultTags=e};c.activeFlow=()=>w.current?.flow??null;c.flush=()=>{Q(w),A()};function l(e,r,t){c(e,r,t)}l.log=c;l.flow=(e,r)=>E(e,r);l.setIdleTimeout=c.setIdleTimeout;l.setFlowName=c.setFlowName;l.setDefaultTags=c.setDefaultTags;l.activeFlow=c.activeFlow;l.flush=c.flush;l.setEnabled=G;l.isEnabled=v;l.subscribe=O;l.toJSON=e=>{let r="trace"in 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(X),...r.meta!==void 0&&{meta:r.meta}}},null,2)};function X(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(u(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(X)),r}function Se(e){return typeof e=="object"&&e!==null&&"trace"in e&&typeof e.trace=="object"&&e.trace!==null}function Z(e){let r=Se(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(j),...r.meta!==void 0&&{meta:r.meta}}},null,2)}function j(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(u(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(j)),r}export{d as NOOP_FLOW,g as NOOP_STEP,l as better,E as createFlow,Y as getRenderer,v as isEnabled,p as normalizeError,u as safeSerialize,xe as setClock,G as setEnabled,we as setIdGenerator,U as setRenderer,O as subscribe,Z as toJSON};
|
package/package.json
CHANGED