@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 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 N=Object.defineProperty;var ee=Object.getOwnPropertyDescriptor;var te=Object.getOwnPropertyNames;var re=Object.prototype.hasOwnProperty;var ne=(e,t)=>{for(var r in t)N(e,r,{get:t[r],enumerable:!0})},oe=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of te(t))!re.call(e,i)&&i!==r&&N(e,i,{get:()=>t[i],enumerable:!(n=ee(t,i))||n.enumerable});return e};var ie=e=>oe(N({},"__esModule",{value:!0}),e);var Oe={};ne(Oe,{NOOP_FLOW:()=>l,NOOP_STEP:()=>m,better:()=>d,createFlow:()=>v,getRenderer:()=>D,isEnabled:()=>C,normalizeError:()=>f,safeSerialize:()=>u,setClock:()=>X,setEnabled:()=>Q,setIdGenerator:()=>V,setRenderer:()=>P,subscribe:()=>L,toJSON:()=>W});module.exports=ie(Oe);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 S=class{constructor(t,r){this._trace=t;this._clock=r;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(t){this._completed||(this._completed=!0,this._trace.end=this._clock.now(),this._trace.state="completed",this._trace.status="error",this._trace.error=f(t))}};var g=()=>{},m={success:g,fail:g},l={step:()=>m,child:()=>l,tag:g,tags:()=>[],setContext:g,getContext:()=>({}),success:g,fail:g,run:(e,t)=>{try{let r=t();return r instanceof Promise?r:Promise.resolve(r)}catch(r){return Promise.reject(r)}}};function J(e){let t=new WeakMap;function r(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(t.has(n))return t.get(n);if(Array.isArray(n)){let s=[];t.set(n,s);for(let o=0;o<n.length;o++)s.push(r(n[o]));return s}let i={};t.set(n,i);for(let s of Object.keys(n))i[s]=r(n[s]);return i}return r(e)}function k(e){if(e===null||typeof e!="object")return e;if(Object.freeze(e),Array.isArray(e))for(let t of e)k(t);else for(let t of Object.keys(e)){let r=e[t];r&&typeof r=="object"&&!Object.isFrozen(r)&&k(r)}return e}function b(e){let t=J(e);return k(t)}var T=new Set;function L(e){return T.add(e),()=>{T.delete(e)}}function se(e){if(T.size!==0)for(let t of T)try{t(e)}catch{}}var y=class e{constructor(t,r,n,i,s){this._trace=t;this._clock=r;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(t,r){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:t,sequence:this._sequence++,state:"running",status:"pending",start:this._clock.now(),data:r,children:[]};return this._trace.steps.push(i),new S(i,this._clock)}child(t){if(this._completed)return l;if(this._options.enabled===!1)return l;let r={id:this._idGen.generate(),name:t,parentId:this._trace.id,state:"running",status:void 0,tags:[],start:this._clock.now(),context:{...this._trace.context},steps:[]};return new e(r,this._clock,this._idGen,this._renderer,{...this._options})}tag(t){this._completed||this._trace.tags.includes(t)||this._trace.tags.push(t)}tags(){return[...this._trace.tags]}setContext(t){this._completed||Object.assign(this._trace.context,t)}getContext(){return{...this._trace.context}}success(){this._finalize("success")}fail(t){this._trace.error=f(t),this._finalize("error")}run(t,r){if(this._completed)return Promise.resolve(r());let n=this.step(t);return Promise.resolve(r()).then(i=>(n.success(),i),i=>{throw n.fail(i),i})}_finalize(t){if(this._completed)return;this._completed=!0,this._trace.end=this._clock.now(),this._trace.state="completed",this._trace.status=t;let r=b(this._trace);se(r),this._renderer.render(r)}};var x=class{generate(){return Math.random().toString(36).slice(2,10)}};var h=class{now(){return Date.now()}};function u(e,t){let r=t?.maxDepth??2,n=t?.maxLength??80,i=new WeakSet;function s(o,c){if(typeof o=="function")return`[Fn:${o.name||"?"}]`;if(typeof o=="symbol")return o.toString();if(c>r)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(p=>s(p,c+1));let _={};for(let p of Object.keys(o)){let E=o[p];_[p]=s(E,c+1)}return _}try{let o=JSON.stringify(s(e,0));return o&&o.length>n?o.slice(0,n)+"\u2026":o}catch{return"[Unserializable]"}}var ae=()=>typeof process<"u"&&process.versions!==void 0&&process.versions.node!==void 0,le=()=>typeof EdgeRuntime<"u",ce=()=>typeof caches<"u"&&"default"in caches,de=()=>typeof window<"u"||typeof self<"u";function q(){return ae()?"node":le()||ce()?"edge":de()?"browser":"node"}var fe="\x1B[0m",ue="\x1B[32m",pe="\x1B[31m";var me="\x1B[36m";var ge="\x1B[2m";function we(){return!(q()==="browser"||typeof process<"u"&&process.env?.NO_COLOR)}var xe=we();function R(e,t){return xe?`${t}${e}${fe}`:e}function z(e){return R(e,ue)}function O(e){return R(e,pe)}function U(e){return R(e,me)}function I(e){return R(e,ge)}function B(e,t){if(t===void 0)return"\u2014";let r=Math.round(t-e);return r>=1e3?`${(r/1e3).toFixed(1)}s`:`${r}ms`}function Y(e,t,r){let n=" ".repeat(t);r.push(`${n}${U("\u2192")} ${e.name}`),e.data!==void 0&&r.push(`${n} ${I("data:")} ${u(e.data,{maxLength:80})}`);let i=B(e.start,e.end);e.status==="success"?r.push(`${n}${z("\u2713")} ${e.name} (${i})`):e.status==="error"?r.push(`${n}${O("\u2717")} ${e.name} (error: ${e.error?.message??"Unknown error"})`):r.push(`${n} ${e.name} (${i})`);for(let s of e.children)Y(s,t+1,r)}var F=class{constructor(){this.name="console"}render(t){let r=[],n=t.tags.length>0?` [${t.tags.join(", ")}]`:"",i=t.status==="error"?"\u{1F525}":"\u{1F680}";r.push(`${i} [flow:${t.name}]${n} (${I("tid:")} ${t.id})`);for(let c of t.steps)Y(c,1,r);let s=B(t.start,t.end),o=t.meta?.maxStepsExceeded?` ${O("\u26A0\uFE0F maxSteps exceeded")}`:"";t.status==="error"?(r.push(`\u{1F3C1} [flow:${t.name}] ${O("failed")} (${s}, error: ${t.error?.message??"Unknown error"})${o}`),console.error(r.join(`
2
- `))):(r.push(`\u{1F3C1} [flow:${t.name}] ${z("success")} (${s})${o}`),console.log(r.join(`
3
- `)))}};var H=null;function P(e){H=e}function D(){return H}function K(e){return H??e}var G=new x,A=new h,_e=new F,M=!0;function Q(e){M=e}function C(){return M}function V(e){G=e}function X(e){A=e}function v(e,t){if(!M)return l;let r=t?.sample??1;if(r<1&&Math.random()>r)return l;if(t?.enabled===!1)return l;let n=K(_e),i={id:G.generate(),name:e,state:"running",tags:t?.tags??[],start:A.now(),context:t?.initialContext?{...t.initialContext}:{},steps:[]};return new y(i,A,G,n,t??{})}var w={idleTimeout:100,flowName:"default",defaultTags:[]},a=null;function Se(e){if(a&&e===void 0&&!a.completed)return a.flow;let t=e??w.flowName,r=v(t,{tags:[...w.defaultTags]});return a={flow:r,timer:null,lastError:null,completed:!1},r}function ke(){a&&(a.timer&&clearTimeout(a.timer),a.timer=setTimeout(()=>{Z()},w.idleTimeout))}function Z(){!a||a.completed||(a.timer&&(clearTimeout(a.timer),a.timer=null),a.completed=!0,a.lastError?a.flow.fail(a.lastError):a.flow.success(),a=null)}function Te(e){return e!==null&&typeof e=="object"&&("flow"in e||"tags"in e)}function $(e,t,r,n){if(!C())return;let i,s;t===void 0?(i=void 0,s=void 0):Te(t)?r===void 0?(i=void 0,s=t):(i=void 0,s={...t,...r}):(i=t,s=r);let o=s?.flow,c=Se(o);if(s?.tags)for(let E of s.tags)c.tag(E);let _=n&&n!=="info"?`${e} [${n}]`:e,p=c.step(_,i);n==="error"?(p.fail(i),a.lastError=i,c.tag("error")):(p.success(),n&&n!=="info"&&c.tag(n)),ke()}var ye=(e,t,r)=>$(e,t,r),d=ye;d.info=(e,t,r)=>$(e,t,r,"info");d.warn=(e,t,r)=>$(e,t,r,"warn");d.error=(e,t,r)=>$(e,t,r,"error");d.setIdleTimeout=e=>{w.idleTimeout=e};d.setFlowName=e=>{w.flowName=e};d.setDefaultTags=e=>{w.defaultTags=e};d.activeFlow=()=>a?.flow??null;d.flush=()=>{Z()};function Re(e){return typeof e=="object"&&e!==null&&"trace"in e&&typeof e.trace=="object"&&e.trace!==null}function W(e){let t=Re(e)?e.trace:e;return JSON.stringify({version:1,flow:{id:t.id,name:t.name,...t.parentId!==void 0&&{parentId:t.parentId},state:t.state,...t.status!==void 0&&{status:t.status},tags:t.tags,start:t.start,...t.end!==void 0&&{end:t.end},...t.error!==void 0&&{error:t.error},context:t.context,steps:t.steps.map(j),...t.meta!==void 0&&{meta:t.meta}}},null,2)}function j(e){let t={id:e.id,name:e.name,sequence:e.sequence,state:e.state,status:e.status,start:e.start};if(e.end!==void 0&&(t.end=e.end),e.data!==void 0)try{t.data=JSON.parse(u(e.data))}catch{t.data="[Circular or unserializable]"}return e.error!==void 0&&(t.error=e.error),e.children.length>0&&(t.children=e.children.map(j)),t}0&&(module.exports={NOOP_FLOW,NOOP_STEP,better,createFlow,getRenderer,isEnabled,normalizeError,safeSerialize,setClock,setEnabled,setIdGenerator,setRenderer,subscribe,toJSON});
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 Log API
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('message', { tags: ['x'] })
181
- * log('message', data, { tags: ['x'], flow: 'custom' })
182
- * log.warn('warning', data)
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
- /** Public Log API */
194
- interface LogAPI {
195
- (message: string, data?: unknown): void;
196
- (message: string, options?: LogCallOptions): void;
197
- (message: string, data?: unknown, options?: LogCallOptions): void;
198
- info: LogFn;
199
- warn: LogFn;
200
- error: LogFn;
201
- setIdleTimeout(ms: number): void;
202
- setFlowName(name: string): void;
203
- setDefaultTags(tags: string[]): void;
204
- activeFlow(): FlowHandle | null;
205
- flush(): void;
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
- type LogFn = (message: string, dataOrOptions?: unknown, options?: LogCallOptions) => void;
208
- declare const better: LogAPI;
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 LogAPI as BetterLogAPI, 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, better, createFlow, getRenderer, isEnabled, normalizeError, safeSerialize, setClock, setEnabled, setIdGenerator, setRenderer, subscribe, toJSON };
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 Log API
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('message', { tags: ['x'] })
181
- * log('message', data, { tags: ['x'], flow: 'custom' })
182
- * log.warn('warning', data)
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
- /** Public Log API */
194
- interface LogAPI {
195
- (message: string, data?: unknown): void;
196
- (message: string, options?: LogCallOptions): void;
197
- (message: string, data?: unknown, options?: LogCallOptions): void;
198
- info: LogFn;
199
- warn: LogFn;
200
- error: LogFn;
201
- setIdleTimeout(ms: number): void;
202
- setFlowName(name: string): void;
203
- setDefaultTags(tags: string[]): void;
204
- activeFlow(): FlowHandle | null;
205
- flush(): void;
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
- type LogFn = (message: string, dataOrOptions?: unknown, options?: LogCallOptions) => void;
208
- declare const better: LogAPI;
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 LogAPI as BetterLogAPI, 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, better, createFlow, getRenderer, isEnabled, normalizeError, safeSerialize, setClock, setEnabled, setIdGenerator, setRenderer, subscribe, toJSON };
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 u(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 S=class{constructor(t,r){this._trace=t;this._clock=r;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(t){this._completed||(this._completed=!0,this._trace.end=this._clock.now(),this._trace.state="completed",this._trace.status="error",this._trace.error=u(t))}};var m=()=>{},g={success:m,fail:m},c={step:()=>g,child:()=>c,tag:m,tags:()=>[],setContext:m,getContext:()=>({}),success:m,fail:m,run:(e,t)=>{try{let r=t();return r instanceof Promise?r:Promise.resolve(r)}catch(r){return Promise.reject(r)}}};function D(e){let t=new WeakMap;function r(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(t.has(n))return t.get(n);if(Array.isArray(n)){let s=[];t.set(n,s);for(let o=0;o<n.length;o++)s.push(r(n[o]));return s}let i={};t.set(n,i);for(let s of Object.keys(n))i[s]=r(n[s]);return i}return r(e)}function k(e){if(e===null||typeof e!="object")return e;if(Object.freeze(e),Array.isArray(e))for(let t of e)k(t);else for(let t of Object.keys(e)){let r=e[t];r&&typeof r=="object"&&!Object.isFrozen(r)&&k(r)}return e}function $(e){let t=D(e);return k(t)}var T=new Set;function G(e){return T.add(e),()=>{T.delete(e)}}function V(e){if(T.size!==0)for(let t of T)try{t(e)}catch{}}var y=class e{constructor(t,r,n,i,s){this._trace=t;this._clock=r;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(t,r){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:t,sequence:this._sequence++,state:"running",status:"pending",start:this._clock.now(),data:r,children:[]};return this._trace.steps.push(i),new S(i,this._clock)}child(t){if(this._completed)return c;if(this._options.enabled===!1)return c;let r={id:this._idGen.generate(),name:t,parentId:this._trace.id,state:"running",status:void 0,tags:[],start:this._clock.now(),context:{...this._trace.context},steps:[]};return new e(r,this._clock,this._idGen,this._renderer,{...this._options})}tag(t){this._completed||this._trace.tags.includes(t)||this._trace.tags.push(t)}tags(){return[...this._trace.tags]}setContext(t){this._completed||Object.assign(this._trace.context,t)}getContext(){return{...this._trace.context}}success(){this._finalize("success")}fail(t){this._trace.error=u(t),this._finalize("error")}run(t,r){if(this._completed)return Promise.resolve(r());let n=this.step(t);return Promise.resolve(r()).then(i=>(n.success(),i),i=>{throw n.fail(i),i})}_finalize(t){if(this._completed)return;this._completed=!0,this._trace.end=this._clock.now(),this._trace.state="completed",this._trace.status=t;let r=$(this._trace);V(r),this._renderer.render(r)}};var x=class{generate(){return Math.random().toString(36).slice(2,10)}};var h=class{now(){return Date.now()}};function p(e,t){let r=t?.maxDepth??2,n=t?.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>r)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(f=>s(f,l+1));let _={};for(let f of Object.keys(o)){let v=o[f];_[f]=s(v,l+1)}return _}try{let o=JSON.stringify(s(e,0));return o&&o.length>n?o.slice(0,n)+"\u2026":o}catch{return"[Unserializable]"}}var X=()=>typeof process<"u"&&process.versions!==void 0&&process.versions.node!==void 0,Z=()=>typeof EdgeRuntime<"u",j=()=>typeof caches<"u"&&"default"in caches,ee=()=>typeof window<"u"||typeof self<"u";function A(){return X()?"node":Z()||j()?"edge":ee()?"browser":"node"}var te="\x1B[0m",re="\x1B[32m",ne="\x1B[31m";var oe="\x1B[36m";var ie="\x1B[2m";function se(){return!(A()==="browser"||typeof process<"u"&&process.env?.NO_COLOR)}var ae=se();function R(e,t){return ae?`${t}${e}${te}`:e}function E(e){return R(e,re)}function O(e){return R(e,ne)}function M(e){return R(e,oe)}function N(e){return R(e,ie)}function W(e,t){if(t===void 0)return"\u2014";let r=Math.round(t-e);return r>=1e3?`${(r/1e3).toFixed(1)}s`:`${r}ms`}function J(e,t,r){let n=" ".repeat(t);r.push(`${n}${M("\u2192")} ${e.name}`),e.data!==void 0&&r.push(`${n} ${N("data:")} ${p(e.data,{maxLength:80})}`);let i=W(e.start,e.end);e.status==="success"?r.push(`${n}${E("\u2713")} ${e.name} (${i})`):e.status==="error"?r.push(`${n}${O("\u2717")} ${e.name} (error: ${e.error?.message??"Unknown error"})`):r.push(`${n} ${e.name} (${i})`);for(let s of e.children)J(s,t+1,r)}var F=class{constructor(){this.name="console"}render(t){let r=[],n=t.tags.length>0?` [${t.tags.join(", ")}]`:"",i=t.status==="error"?"\u{1F525}":"\u{1F680}";r.push(`${i} [flow:${t.name}]${n} (${N("tid:")} ${t.id})`);for(let l of t.steps)J(l,1,r);let s=W(t.start,t.end),o=t.meta?.maxStepsExceeded?` ${O("\u26A0\uFE0F maxSteps exceeded")}`:"";t.status==="error"?(r.push(`\u{1F3C1} [flow:${t.name}] ${O("failed")} (${s}, error: ${t.error?.message??"Unknown error"})${o}`),console.error(r.join(`
2
- `))):(r.push(`\u{1F3C1} [flow:${t.name}] ${E("success")} (${s})${o}`),console.log(r.join(`
3
- `)))}};var b=null;function q(e){b=e}function U(){return b}function B(e){return b??e}var L=new x,z=new h,de=new F,I=!0;function fe(e){I=e}function H(){return I}function ue(e){L=e}function pe(e){z=e}function P(e,t){if(!I)return c;let r=t?.sample??1;if(r<1&&Math.random()>r)return c;if(t?.enabled===!1)return c;let n=B(de),i={id:L.generate(),name:e,state:"running",tags:t?.tags??[],start:z.now(),context:t?.initialContext?{...t.initialContext}:{},steps:[]};return new y(i,z,L,n,t??{})}var w={idleTimeout:100,flowName:"default",defaultTags:[]},a=null;function me(e){if(a&&e===void 0&&!a.completed)return a.flow;let t=e??w.flowName,r=P(t,{tags:[...w.defaultTags]});return a={flow:r,timer:null,lastError:null,completed:!1},r}function ge(){a&&(a.timer&&clearTimeout(a.timer),a.timer=setTimeout(()=>{Y()},w.idleTimeout))}function Y(){!a||a.completed||(a.timer&&(clearTimeout(a.timer),a.timer=null),a.completed=!0,a.lastError?a.flow.fail(a.lastError):a.flow.success(),a=null)}function we(e){return e!==null&&typeof e=="object"&&("flow"in e||"tags"in e)}function C(e,t,r,n){if(!H())return;let i,s;t===void 0?(i=void 0,s=void 0):we(t)?r===void 0?(i=void 0,s=t):(i=void 0,s={...t,...r}):(i=t,s=r);let o=s?.flow,l=me(o);if(s?.tags)for(let v of s.tags)l.tag(v);let _=n&&n!=="info"?`${e} [${n}]`:e,f=l.step(_,i);n==="error"?(f.fail(i),a.lastError=i,l.tag("error")):(f.success(),n&&n!=="info"&&l.tag(n)),ge()}var xe=(e,t,r)=>C(e,t,r),d=xe;d.info=(e,t,r)=>C(e,t,r,"info");d.warn=(e,t,r)=>C(e,t,r,"warn");d.error=(e,t,r)=>C(e,t,r,"error");d.setIdleTimeout=e=>{w.idleTimeout=e};d.setFlowName=e=>{w.flowName=e};d.setDefaultTags=e=>{w.defaultTags=e};d.activeFlow=()=>a?.flow??null;d.flush=()=>{Y()};function he(e){return typeof e=="object"&&e!==null&&"trace"in e&&typeof e.trace=="object"&&e.trace!==null}function K(e){let t=he(e)?e.trace:e;return JSON.stringify({version:1,flow:{id:t.id,name:t.name,...t.parentId!==void 0&&{parentId:t.parentId},state:t.state,...t.status!==void 0&&{status:t.status},tags:t.tags,start:t.start,...t.end!==void 0&&{end:t.end},...t.error!==void 0&&{error:t.error},context:t.context,steps:t.steps.map(Q),...t.meta!==void 0&&{meta:t.meta}}},null,2)}function Q(e){let t={id:e.id,name:e.name,sequence:e.sequence,state:e.state,status:e.status,start:e.start};if(e.end!==void 0&&(t.end=e.end),e.data!==void 0)try{t.data=JSON.parse(p(e.data))}catch{t.data="[Circular or unserializable]"}return e.error!==void 0&&(t.error=e.error),e.children.length>0&&(t.children=e.children.map(Q)),t}export{c as NOOP_FLOW,g as NOOP_STEP,d as better,P as createFlow,U as getRenderer,H as isEnabled,u as normalizeError,p as safeSerialize,pe as setClock,fe as setEnabled,ue as setIdGenerator,q as setRenderer,G as subscribe,K as toJSON};
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
@@ -52,5 +52,5 @@
52
52
  "@typescript-eslint/eslint-plugin": "8.58.0",
53
53
  "@types/node": "20.11.0"
54
54
  },
55
- "version": "0.5.1"
55
+ "version": "0.7.0"
56
56
  }