@better-logger/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +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
package/dist/index.cjs ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";var y=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var W=Object.prototype.hasOwnProperty;var q=(e,t)=>{for(var r in t)y(e,r,{get:t[r],enumerable:!0})},J=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of M(t))!W.call(e,s)&&s!==r&&y(e,s,{get:()=>t[s],enumerable:!(o=A(t,s))||o.enumerable});return e};var U=e=>J(y({},"__esModule",{value:!0}),e);var ie={};q(ie,{createFlow:()=>G,subscribe:()=>T,toJSON:()=>E});module.exports=U(ie);function l(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 x=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=l(t))}};var c=()=>{},u={success:c,fail:c},a={step:()=>u,child:()=>a,tag:c,tags:()=>[],setContext:c,getContext:()=>({}),success:c,fail:c};function N(e){let t=new WeakMap;function r(o){if(o===null||typeof o!="object")return o;if(o instanceof Date)return new Date(o.getTime());if(o instanceof RegExp)return new RegExp(o.source,o.flags);if(t.has(o))return t.get(o);if(Array.isArray(o)){let i=[];t.set(o,i);for(let n=0;n<o.length;n++)i.push(r(o[n]));return i}let s={};t.set(o,s);for(let i of Object.keys(o))s[i]=r(o[i]);return s}return r(e)}function h(e){if(e===null||typeof e!="object")return e;if(Object.freeze(e),Array.isArray(e))for(let t of e)h(t);else for(let t of Object.keys(e)){let r=e[t];r&&typeof r=="object"&&!Object.isFrozen(r)&&h(r)}return e}function R(e){let t=N(e);return h(t)}var _=new Set;function T(e){return _.add(e),()=>{_.delete(e)}}function Y(e){if(_.size!==0)for(let t of _)try{t(e)}catch{}}var S=class e{constructor(t,r,o,s,i){this._trace=t;this._clock=r;this._idGen=o;this._renderer=s;this._options=i;this._completed=!1;this._sequence=0}get isCompleted(){return this._completed}get trace(){return this._trace}step(t,r){if(this._completed)return u;let o=this._options.maxSteps;if(o!==void 0&&this._trace.steps.length>=o)return this._trace.meta===void 0?this._trace.meta={maxStepsExceeded:!0}:this._trace.meta.maxStepsExceeded=!0,u;let s={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(s),new x(s,this._clock)}child(t){if(this._completed)return a;if(this._options.enabled===!1)return a;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=l(t),this._finalize("error")}_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=R(this._trace);Y(r),this._renderer.render(r)}};var f=class{generate(){return Math.random().toString(36).slice(2,10)}};var p=class{now(){return Date.now()}};function m(e,t){let r=t?.maxDepth??2,o=t?.maxLength??80,s=new WeakSet;function i(n,d){if(typeof n=="function")return`[Fn:${n.name||"?"}]`;if(typeof n=="symbol")return n.toString();if(d>r)return n&&typeof n=="object"?"[...]":n;if(n===null||typeof n!="object")return n;if(n instanceof Map)return`{Map(${n.size})}`;if(n instanceof Set)return`{Set(${n.size})}`;if(n instanceof Date)return n.toISOString();if(n instanceof RegExp)return n.toString();if(s.has(n))return"[Circular]";if(s.add(n),Array.isArray(n))return n.map(w=>i(w,d+1));let v={};for(let w of Object.keys(n)){let P=n[w];v[w]=i(P,d+1)}return v}try{let n=JSON.stringify(i(e,0));return n&&n.length>o?n.slice(0,o)+"\u2026":n}catch{return"[Unserializable]"}}var B=()=>typeof process<"u"&&process.versions!==void 0&&process.versions.node!==void 0,K=()=>typeof EdgeRuntime<"u",Q=()=>typeof caches<"u"&&"default"in caches,V=()=>typeof window<"u"||typeof self<"u";function C(){return B()?"node":K()||Q()?"edge":V()?"browser":"node"}var X="\x1B[0m",Z="\x1B[32m",j="\x1B[31m";var ee="\x1B[36m";var te="\x1B[2m";function re(){return!(C()==="browser"||typeof process<"u"&&process.env?.NO_COLOR)}var ne=re();function F(e,t){return ne?`${t}${e}${X}`:e}function O(e){return F(e,Z)}function k(e){return F(e,j)}function z(e){return F(e,ee)}function $(e){return F(e,te)}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 H(e,t,r){let o=" ".repeat(t);r.push(`${o}${z("\u2192")} ${e.name}`),e.data!==void 0&&r.push(`${o} ${$("data:")} ${m(e.data,{maxLength:80})}`);let s=b(e.start,e.end);e.status==="success"?r.push(`${o}${O("\u2713")} ${e.name} (${s})`):e.status==="error"?r.push(`${o}${k("\u2717")} ${e.name} (error: ${e.error?.message??"Unknown error"})`):r.push(`${o} ${e.name} (${s})`);for(let i of e.children)H(i,t+1,r)}var g=class{render(t){let r=[],o=t.tags.length>0?` [${t.tags.join(", ")}]`:"",s=t.status==="error"?"\u{1F525}":"\u{1F680}";r.push(`${s} [flow:${t.name}]${o} (${$("tid:")} ${t.id})`);for(let d of t.steps)H(d,1,r);let i=b(t.start,t.end),n=t.meta?.maxStepsExceeded?` ${k("\u26A0\uFE0F maxSteps exceeded")}`:"";t.status==="error"?(r.push(`\u{1F3C1} [flow:${t.name}] ${k("failed")} (${i}, error: ${t.error?.message??"Unknown error"})${n}`),console.error(r.join(`
2
+ `))):(r.push(`\u{1F3C1} [flow:${t.name}] ${O("success")} (${i})${n}`),console.log(r.join(`
3
+ `)))}};var L=new f,D=new p,oe=new g;function G(e,t){if(t?.enabled===!1)return a;let r={id:L.generate(),name:e,state:"running",tags:t?.tags??[],start:D.now(),context:{},steps:[]};return new S(r,D,L,oe,t??{})}function se(e){return typeof e=="object"&&e!==null&&"trace"in e&&typeof e.trace=="object"&&e.trace!==null}function E(e){let t=se(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(I),...t.meta!==void 0&&{meta:t.meta}}},null,2)}function I(e){let t={id:e.id,name:e.name,sequence:e.sequence,state:e.state,status:e.status,start:e.start};return e.end!==void 0&&(t.end=e.end),e.data!==void 0&&(t.data=m(e.data)),e.error!==void 0&&(t.error=e.error),e.children.length>0&&(t.children=e.children.map(I)),t}0&&(module.exports={createFlow,subscribe,toJSON});
@@ -0,0 +1,93 @@
1
+ /** Lifecycle state for flows and steps */
2
+ type FlowState = 'running' | 'completed';
3
+ /** Final status of a completed flow */
4
+ type FlowStatus = 'success' | 'error';
5
+ /** Final status of a completed step */
6
+ type StepStatus = 'pending' | 'success' | 'error';
7
+ /** Normalized error shape โ€” all errors converge to this */
8
+ interface NormalizedError {
9
+ name: string;
10
+ message: string;
11
+ stack?: string;
12
+ }
13
+ /** Complete execution flow trace */
14
+ interface FlowTrace {
15
+ id: string;
16
+ name: string;
17
+ parentId?: string;
18
+ state: FlowState;
19
+ status?: FlowStatus;
20
+ tags: string[];
21
+ start: number;
22
+ end?: number;
23
+ error?: NormalizedError;
24
+ context: Record<string, unknown>;
25
+ steps: StepTrace[];
26
+ meta?: {
27
+ maxStepsExceeded: boolean;
28
+ };
29
+ }
30
+ /** Individual step within a flow */
31
+ interface StepTrace {
32
+ id: string;
33
+ name: string;
34
+ sequence: number;
35
+ state: FlowState;
36
+ status: StepStatus;
37
+ start: number;
38
+ end?: number;
39
+ data?: unknown;
40
+ error?: NormalizedError;
41
+ children: StepTrace[];
42
+ }
43
+ /** Options for creating a flow */
44
+ interface FlowOptions {
45
+ enabled?: boolean;
46
+ tags?: string[];
47
+ maxSteps?: number;
48
+ }
49
+
50
+ /** Public StepHandle interface */
51
+ interface StepHandle {
52
+ success(): void;
53
+ fail(error?: unknown): void;
54
+ }
55
+
56
+ /** Public FlowHandle interface */
57
+ interface FlowHandle {
58
+ step(name: string, data?: unknown): StepHandle;
59
+ child(name: string): FlowHandle;
60
+ tag(tag: string): void;
61
+ tags(): string[];
62
+ setContext(context: Record<string, unknown>): void;
63
+ getContext(): Record<string, unknown>;
64
+ success(): void;
65
+ fail(error?: unknown): void;
66
+ }
67
+ /** Listener type for subscribe */
68
+ type FlowListener = (flow: Readonly<FlowTrace>) => void;
69
+ /**
70
+ * Register a listener that receives frozen snapshots on flow completion.
71
+ * Zero overhead when no listeners registered.
72
+ */
73
+ declare function subscribe(listener: FlowListener): () => void;
74
+
75
+ /**
76
+ * Create a new execution flow with a human-readable name and optional configuration.
77
+ *
78
+ * @param name - Human-readable flow name
79
+ * @param options - Optional configuration (enabled, tags, maxSteps)
80
+ * @returns FlowHandle for interacting with the flow
81
+ */
82
+ declare function createFlow(name: string, options?: FlowOptions): FlowHandle;
83
+
84
+ /**
85
+ * Serialize a FlowTrace to versioned JSON string.
86
+ * Accepts either a FlowTrace directly or a FlowHandle (extracts .trace).
87
+ * Omits undefined values for clean output.
88
+ */
89
+ declare function toJSON(input: Readonly<FlowTrace> | {
90
+ trace: Readonly<FlowTrace>;
91
+ }): string;
92
+
93
+ export { type FlowOptions, type FlowState, type FlowStatus, type FlowTrace, type NormalizedError, type StepStatus, type StepTrace, createFlow, subscribe, toJSON };
@@ -0,0 +1,93 @@
1
+ /** Lifecycle state for flows and steps */
2
+ type FlowState = 'running' | 'completed';
3
+ /** Final status of a completed flow */
4
+ type FlowStatus = 'success' | 'error';
5
+ /** Final status of a completed step */
6
+ type StepStatus = 'pending' | 'success' | 'error';
7
+ /** Normalized error shape โ€” all errors converge to this */
8
+ interface NormalizedError {
9
+ name: string;
10
+ message: string;
11
+ stack?: string;
12
+ }
13
+ /** Complete execution flow trace */
14
+ interface FlowTrace {
15
+ id: string;
16
+ name: string;
17
+ parentId?: string;
18
+ state: FlowState;
19
+ status?: FlowStatus;
20
+ tags: string[];
21
+ start: number;
22
+ end?: number;
23
+ error?: NormalizedError;
24
+ context: Record<string, unknown>;
25
+ steps: StepTrace[];
26
+ meta?: {
27
+ maxStepsExceeded: boolean;
28
+ };
29
+ }
30
+ /** Individual step within a flow */
31
+ interface StepTrace {
32
+ id: string;
33
+ name: string;
34
+ sequence: number;
35
+ state: FlowState;
36
+ status: StepStatus;
37
+ start: number;
38
+ end?: number;
39
+ data?: unknown;
40
+ error?: NormalizedError;
41
+ children: StepTrace[];
42
+ }
43
+ /** Options for creating a flow */
44
+ interface FlowOptions {
45
+ enabled?: boolean;
46
+ tags?: string[];
47
+ maxSteps?: number;
48
+ }
49
+
50
+ /** Public StepHandle interface */
51
+ interface StepHandle {
52
+ success(): void;
53
+ fail(error?: unknown): void;
54
+ }
55
+
56
+ /** Public FlowHandle interface */
57
+ interface FlowHandle {
58
+ step(name: string, data?: unknown): StepHandle;
59
+ child(name: string): FlowHandle;
60
+ tag(tag: string): void;
61
+ tags(): string[];
62
+ setContext(context: Record<string, unknown>): void;
63
+ getContext(): Record<string, unknown>;
64
+ success(): void;
65
+ fail(error?: unknown): void;
66
+ }
67
+ /** Listener type for subscribe */
68
+ type FlowListener = (flow: Readonly<FlowTrace>) => void;
69
+ /**
70
+ * Register a listener that receives frozen snapshots on flow completion.
71
+ * Zero overhead when no listeners registered.
72
+ */
73
+ declare function subscribe(listener: FlowListener): () => void;
74
+
75
+ /**
76
+ * Create a new execution flow with a human-readable name and optional configuration.
77
+ *
78
+ * @param name - Human-readable flow name
79
+ * @param options - Optional configuration (enabled, tags, maxSteps)
80
+ * @returns FlowHandle for interacting with the flow
81
+ */
82
+ declare function createFlow(name: string, options?: FlowOptions): FlowHandle;
83
+
84
+ /**
85
+ * Serialize a FlowTrace to versioned JSON string.
86
+ * Accepts either a FlowTrace directly or a FlowHandle (extracts .trace).
87
+ * Omits undefined values for clean output.
88
+ */
89
+ declare function toJSON(input: Readonly<FlowTrace> | {
90
+ trace: Readonly<FlowTrace>;
91
+ }): string;
92
+
93
+ export { type FlowOptions, type FlowState, type FlowStatus, type FlowTrace, type NormalizedError, type StepStatus, type StepTrace, createFlow, subscribe, toJSON };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ function l(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 x=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=l(t))}};var c=()=>{},u={success:c,fail:c},a={step:()=>u,child:()=>a,tag:c,tags:()=>[],setContext:c,getContext:()=>({}),success:c,fail:c};function $(e){let t=new WeakMap;function r(o){if(o===null||typeof o!="object")return o;if(o instanceof Date)return new Date(o.getTime());if(o instanceof RegExp)return new RegExp(o.source,o.flags);if(t.has(o))return t.get(o);if(Array.isArray(o)){let s=[];t.set(o,s);for(let n=0;n<o.length;n++)s.push(r(o[n]));return s}let i={};t.set(o,i);for(let s of Object.keys(o))i[s]=r(o[s]);return i}return r(e)}function h(e){if(e===null||typeof e!="object")return e;if(Object.freeze(e),Array.isArray(e))for(let t of e)h(t);else for(let t of Object.keys(e)){let r=e[t];r&&typeof r=="object"&&!Object.isFrozen(r)&&h(r)}return e}function y(e){let t=$(e);return h(t)}var _=new Set;function E(e){return _.add(e),()=>{_.delete(e)}}function I(e){if(_.size!==0)for(let t of _)try{t(e)}catch{}}var S=class e{constructor(t,r,o,i,s){this._trace=t;this._clock=r;this._idGen=o;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 u;let o=this._options.maxSteps;if(o!==void 0&&this._trace.steps.length>=o)return this._trace.meta===void 0?this._trace.meta={maxStepsExceeded:!0}:this._trace.meta.maxStepsExceeded=!0,u;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 x(i,this._clock)}child(t){if(this._completed)return a;if(this._options.enabled===!1)return a;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=l(t),this._finalize("error")}_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=y(this._trace);I(r),this._renderer.render(r)}};var f=class{generate(){return Math.random().toString(36).slice(2,10)}};var p=class{now(){return Date.now()}};function m(e,t){let r=t?.maxDepth??2,o=t?.maxLength??80,i=new WeakSet;function s(n,d){if(typeof n=="function")return`[Fn:${n.name||"?"}]`;if(typeof n=="symbol")return n.toString();if(d>r)return n&&typeof n=="object"?"[...]":n;if(n===null||typeof n!="object")return n;if(n instanceof Map)return`{Map(${n.size})}`;if(n instanceof Set)return`{Set(${n.size})}`;if(n instanceof Date)return n.toISOString();if(n instanceof RegExp)return n.toString();if(i.has(n))return"[Circular]";if(i.add(n),Array.isArray(n))return n.map(w=>s(w,d+1));let O={};for(let w of Object.keys(n)){let G=n[w];O[w]=s(G,d+1)}return O}try{let n=JSON.stringify(s(e,0));return n&&n.length>o?n.slice(0,o)+"\u2026":n}catch{return"[Unserializable]"}}var P=()=>typeof process<"u"&&process.versions!==void 0&&process.versions.node!==void 0,A=()=>typeof EdgeRuntime<"u",M=()=>typeof caches<"u"&&"default"in caches,W=()=>typeof window<"u"||typeof self<"u";function v(){return P()?"node":A()||M()?"edge":W()?"browser":"node"}var q="\x1B[0m",J="\x1B[32m",U="\x1B[31m";var Y="\x1B[36m";var B="\x1B[2m";function K(){return!(v()==="browser"||typeof process<"u"&&process.env?.NO_COLOR)}var Q=K();function F(e,t){return Q?`${t}${e}${q}`:e}function R(e){return F(e,J)}function k(e){return F(e,U)}function N(e){return F(e,Y)}function T(e){return F(e,B)}function C(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 z(e,t,r){let o=" ".repeat(t);r.push(`${o}${N("\u2192")} ${e.name}`),e.data!==void 0&&r.push(`${o} ${T("data:")} ${m(e.data,{maxLength:80})}`);let i=C(e.start,e.end);e.status==="success"?r.push(`${o}${R("\u2713")} ${e.name} (${i})`):e.status==="error"?r.push(`${o}${k("\u2717")} ${e.name} (error: ${e.error?.message??"Unknown error"})`):r.push(`${o} ${e.name} (${i})`);for(let s of e.children)z(s,t+1,r)}var g=class{render(t){let r=[],o=t.tags.length>0?` [${t.tags.join(", ")}]`:"",i=t.status==="error"?"\u{1F525}":"\u{1F680}";r.push(`${i} [flow:${t.name}]${o} (${T("tid:")} ${t.id})`);for(let d of t.steps)z(d,1,r);let s=C(t.start,t.end),n=t.meta?.maxStepsExceeded?` ${k("\u26A0\uFE0F maxSteps exceeded")}`:"";t.status==="error"?(r.push(`\u{1F3C1} [flow:${t.name}] ${k("failed")} (${s}, error: ${t.error?.message??"Unknown error"})${n}`),console.error(r.join(`
2
+ `))):(r.push(`\u{1F3C1} [flow:${t.name}] ${R("success")} (${s})${n}`),console.log(r.join(`
3
+ `)))}};var b=new f,H=new p,V=new g;function X(e,t){if(t?.enabled===!1)return a;let r={id:b.generate(),name:e,state:"running",tags:t?.tags??[],start:H.now(),context:{},steps:[]};return new S(r,H,b,V,t??{})}function Z(e){return typeof e=="object"&&e!==null&&"trace"in e&&typeof e.trace=="object"&&e.trace!==null}function L(e){let t=Z(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(D),...t.meta!==void 0&&{meta:t.meta}}},null,2)}function D(e){let t={id:e.id,name:e.name,sequence:e.sequence,state:e.state,status:e.status,start:e.start};return e.end!==void 0&&(t.end=e.end),e.data!==void 0&&(t.data=m(e.data)),e.error!==void 0&&(t.error=e.error),e.children.length>0&&(t.children=e.children.map(D)),t}export{X as createFlow,E as subscribe,L as toJSON};
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapter/error-normalizer.ts","../src/adapter/step-handle.ts","../src/adapter/noop-singletons.ts","../src/renderer/snapshot.ts","../src/adapter/flow-handle.ts","../src/adapter/id-generator.ts","../src/adapter/clock.ts","../src/adapter/safe-serializer.ts","../src/renderer/ansi.ts","../src/renderer/console.ts","../src/adapter/index.ts","../src/renderer/json.ts"],"sourcesContent":["import { NormalizedError } from '../core/types'\n\n/**\n * Normalizes any error input into a consistent NormalizedError shape.\n *\n * Rules:\n * - Error instance โ†’ { name, message, stack }\n * - String โ†’ { name: 'Error', message }\n * - Other โ†’ { name: 'Error', message: String(input) }\n * - undefined/no arg โ†’ { name: 'Error', message: 'Unknown error' }\n */\nexport function normalizeError(input: unknown): NormalizedError {\n if (input === undefined) {\n return { name: 'Error', message: 'Unknown error' }\n }\n\n if (input instanceof Error) {\n return {\n name: input.name,\n message: input.message,\n stack: input.stack,\n }\n }\n\n if (typeof input === 'string') {\n return { name: 'Error', message: input }\n }\n\n return { name: 'Error', message: String(input) }\n}\n","import { StepTrace, FlowState, StepStatus } from '../core/types'\nimport { Clock } from './clock'\nimport { normalizeError } from './error-normalizer'\n\n/** Public StepHandle interface */\nexport interface StepHandle {\n success(): void\n fail(error?: unknown): void\n}\n\n/** Internal implementation โ€” not exported publicly */\nexport class StepHandleImpl implements StepHandle {\n private _completed = false\n\n constructor(\n private readonly _trace: StepTrace,\n private readonly _clock: Clock\n ) {}\n\n /** Returns true if this step has been finalized */\n get isCompleted(): boolean {\n return this._completed\n }\n\n /** Returns the underlying trace for inspection */\n get trace(): Readonly<StepTrace> {\n return this._trace\n }\n\n success(): void {\n if (this._completed) return // guarded finalization โ€” silent no-op\n this._completed = true\n this._trace.end = this._clock.now()\n this._trace.state = 'completed' as FlowState\n this._trace.status = 'success' as StepStatus\n }\n\n fail(error?: unknown): void {\n if (this._completed) return // guarded finalization โ€” silent no-op\n this._completed = true\n this._trace.end = this._clock.now()\n this._trace.state = 'completed' as FlowState\n this._trace.status = 'error' as StepStatus\n this._trace.error = normalizeError(error)\n }\n}\n","/** No-op function placeholder */\nconst noop = () => {}\n\n/**\n * NoOp StepHandle shape โ€” reused across all disabled/guarded paths.\n * Defined inline to avoid circular dependency with flow-handle.ts.\n */\nexport interface NoOpStepHandle {\n success(): void\n fail(error?: unknown): void\n}\n\n/**\n * NoOp FlowHandle shape โ€” reused across all disabled/guarded paths.\n * Defined inline to avoid circular dependency with flow-handle.ts.\n */\nexport interface NoOpFlowHandle {\n step(): NoOpStepHandle\n child(): NoOpFlowHandle\n tag(name: string): void\n tags(): string[]\n setContext(context: Record<string, unknown>): void\n getContext(): Record<string, unknown>\n success(): void\n fail(error?: unknown): void\n}\n\nexport const NOOP_STEP: NoOpStepHandle = {\n success: noop,\n fail: noop,\n}\n\nexport const NOOP_FLOW: NoOpFlowHandle = {\n step: () => NOOP_STEP,\n child: () => NOOP_FLOW,\n tag: noop,\n tags: () => [],\n setContext: noop,\n getContext: () => ({}),\n success: noop,\n fail: noop,\n}\n","/** Deep clone an object with circular reference protection */\nexport function cloneDeep<T>(obj: T): T {\n const seen = new WeakMap<object, unknown>()\n\n function clone(value: unknown): unknown {\n if (value === null || typeof value !== 'object') return value\n if (value instanceof Date) return new Date(value.getTime())\n if (value instanceof RegExp) return new RegExp(value.source, value.flags)\n\n if (seen.has(value as object)) {\n return seen.get(value as object)\n }\n\n if (Array.isArray(value)) {\n const arr: unknown[] = []\n seen.set(value as object, arr)\n return arr.map((item) => clone(item))\n }\n\n const result: Record<string, unknown> = {}\n seen.set(value as object, result)\n\n for (const key of Object.keys(value)) {\n result[key] = clone((value as Record<string, unknown>)[key])\n }\n return result as T\n }\n\n return clone(obj) as T\n}\n\n/** Recursively freeze an object via Object.freeze */\nexport function deepFreeze<T>(obj: T): Readonly<T> {\n if (obj === null || typeof obj !== 'object') return obj\n\n Object.freeze(obj)\n\n if (Array.isArray(obj)) {\n for (const item of obj) {\n deepFreeze(item)\n }\n } else {\n for (const key of Object.keys(obj)) {\n const value = (obj as Record<string, unknown>)[key]\n if (value && typeof value === 'object' && !Object.isFrozen(value)) {\n deepFreeze(value)\n }\n }\n }\n\n return obj\n}\n\n/** Create an immutable snapshot of a FlowTrace */\nexport function createSnapshot<T extends object>(trace: T): Readonly<T> {\n const cloned = cloneDeep(trace)\n return deepFreeze(cloned)\n}\n","import {\n FlowTrace,\n StepTrace,\n FlowOptions,\n FlowState,\n FlowStatus,\n} from '../core/types'\nimport { Clock } from './clock'\nimport { IdGenerator } from './id-generator'\nimport { StepHandle, StepHandleImpl } from './step-handle'\nimport { NOOP_FLOW, NOOP_STEP } from './noop-singletons'\nimport { normalizeError } from './error-normalizer'\nimport { FlowRenderer } from '../renderer/types'\nimport { createSnapshot } from '../renderer/snapshot'\n\n/** Public FlowHandle interface */\nexport interface FlowHandle {\n step(name: string, data?: unknown): StepHandle\n child(name: string): FlowHandle\n tag(tag: string): void\n tags(): string[]\n setContext(context: Record<string, unknown>): void\n getContext(): Record<string, unknown>\n success(): void\n fail(error?: unknown): void\n}\n\n/** Listener type for subscribe */\nexport type FlowListener = (flow: Readonly<FlowTrace>) => void\n\n/** Global subscriber registry */\nconst _listeners = new Set<FlowListener>()\n\n/**\n * Register a listener that receives frozen snapshots on flow completion.\n * Zero overhead when no listeners registered.\n */\nexport function subscribe(listener: FlowListener): () => void {\n _listeners.add(listener)\n return () => {\n _listeners.delete(listener)\n }\n}\n\n/**\n * Fire all registered listeners with a frozen snapshot.\n * Internal โ€” called by FlowHandleImpl on completion.\n */\nexport function _fireSubscribers(snapshot: Readonly<FlowTrace>): void {\n if (_listeners.size === 0) return // zero overhead guard\n for (const listener of _listeners) {\n try {\n listener(snapshot)\n } catch {\n // Listener errors must not crash the flow or other listeners\n }\n }\n}\n\n/** Internal FlowHandle implementation */\nexport class FlowHandleImpl implements FlowHandle {\n private _completed = false\n private _sequence = 0\n\n constructor(\n private readonly _trace: FlowTrace,\n private readonly _clock: Clock,\n private readonly _idGen: IdGenerator,\n private readonly _renderer: FlowRenderer,\n private readonly _options: FlowOptions\n ) {}\n\n /** Returns true if this flow has been finalized */\n get isCompleted(): boolean {\n return this._completed\n }\n\n /** Returns the underlying trace for inspection */\n get trace(): Readonly<FlowTrace> {\n return this._trace\n }\n\n step(name: string, data?: unknown): StepHandle {\n // Guard: completed flow โ†’ return NOOP_STEP\n if (this._completed) return NOOP_STEP\n\n // Guard: maxSteps exceeded\n const maxSteps = this._options.maxSteps\n if (maxSteps !== undefined && this._trace.steps.length >= maxSteps) {\n if (this._trace.meta === undefined) {\n this._trace.meta = { maxStepsExceeded: true }\n } else {\n this._trace.meta.maxStepsExceeded = true\n }\n return NOOP_STEP\n }\n\n const stepTrace: StepTrace = {\n id: this._idGen.generate(),\n name,\n sequence: this._sequence++,\n state: 'running',\n status: 'pending',\n start: this._clock.now(),\n data,\n children: [],\n }\n\n this._trace.steps.push(stepTrace)\n return new StepHandleImpl(stepTrace, this._clock)\n }\n\n child(name: string): FlowHandle {\n // Guard: completed flow โ†’ return NOOP_FLOW\n if (this._completed) return NOOP_FLOW\n\n // If this flow is disabled, child is also disabled\n if (this._options.enabled === false) return NOOP_FLOW\n\n const childTrace: FlowTrace = {\n id: this._idGen.generate(),\n name,\n parentId: this._trace.id,\n state: 'running',\n status: undefined,\n tags: [],\n start: this._clock.now(),\n context: { ...this._trace.context }, // shallow copy โ€” child inherits\n steps: [],\n }\n\n // Return a new FlowHandle for the child, sharing renderer and dependencies\n return new FlowHandleImpl(\n childTrace,\n this._clock,\n this._idGen,\n this._renderer,\n { ...this._options }\n )\n }\n\n tag(name: string): void {\n if (this._completed) return\n // Deduplicate tags\n if (!this._trace.tags.includes(name)) {\n this._trace.tags.push(name)\n }\n }\n\n tags(): string[] {\n return [...this._trace.tags] // return copy\n }\n\n setContext(context: Record<string, unknown>): void {\n if (this._completed) return\n // Shallow merge โ€” new keys override, existing keys preserved\n Object.assign(this._trace.context, context)\n }\n\n getContext(): Record<string, unknown> {\n return { ...this._trace.context } // return copy\n }\n\n success(): void {\n this._finalize('success')\n }\n\n fail(error?: unknown): void {\n this._trace.error = normalizeError(error)\n this._finalize('error')\n }\n\n private _finalize(status: FlowStatus): void {\n if (this._completed) return // guarded finalization โ€” silent no-op\n this._completed = true\n\n this._trace.end = this._clock.now()\n this._trace.state = 'completed' as FlowState\n this._trace.status = status\n\n // Create immutable snapshot\n const snapshot = createSnapshot(this._trace)\n\n // Fire subscribers before render\n _fireSubscribers(snapshot)\n\n // Atomic render\n this._renderer.render(snapshot)\n }\n}\n","/** Contract for ID generation strategies */\nexport interface IdGenerator {\n generate(): string\n}\n\n/** Default ID generator โ€” 8-char alphanumeric via Math.random */\nexport class RandomIdGenerator implements IdGenerator {\n generate(): string {\n return Math.random().toString(36).slice(2, 10)\n }\n}\n","/** Contract for clock implementations */\nexport interface Clock {\n now(): number\n}\n\n/** Universal clock โ€” Date.now() works everywhere */\nexport class DateClock implements Clock {\n now(): number {\n return Date.now()\n }\n}\n","export interface SerializeOptions {\n maxDepth?: number\n maxLength?: number\n}\n\n/**\n * Safely serialize arbitrary data to a JSON string.\n *\n * Protects against:\n * - Circular references โ†’ replaced with \"[Circular]\"\n * - Excessive nesting โ†’ maxDepth: 2, strips beyond\n * - Excessive output length โ†’ maxLength: 80, truncates with \"โ€ฆ\"\n * - Non-serializable types โ†’ Map, Set, Symbol, Function โ†’ stringified\n * - Never throws\n */\nexport function safeSerialize(\n data: unknown,\n options?: SerializeOptions\n): string {\n const maxDepth = options?.maxDepth ?? 2\n const maxLength = options?.maxLength ?? 80\n const seen = new WeakSet<object>()\n\n function replacer(key: string, value: unknown): unknown {\n if (value === null) return null\n if (value === undefined) return undefined\n\n if (typeof value === 'function') {\n return `[Function: ${value.name || 'anonymous'}]`\n }\n\n if (typeof value === 'symbol') {\n return value.toString()\n }\n\n if (value instanceof Map) {\n return `{Map(${value.size})}`\n }\n\n if (value instanceof Set) {\n return `{Set(${value.size})}`\n }\n\n if (value instanceof Date) {\n return value.toISOString()\n }\n\n if (value instanceof RegExp) {\n return value.toString()\n }\n\n if (typeof value === 'object') {\n if (seen.has(value as object)) {\n return '[Circular]'\n }\n }\n\n return value\n }\n\n function serializeWithDepth(obj: unknown, depth: number): unknown {\n if (depth > maxDepth) {\n if (obj && typeof obj === 'object') {\n return '[...]'\n }\n return obj\n }\n\n if (obj === null || typeof obj !== 'object') {\n return obj\n }\n\n if (obj instanceof Map) {\n return `{Map(${obj.size})}`\n }\n\n if (obj instanceof Set) {\n return `{Set(${obj.size})}`\n }\n\n if (obj instanceof Date) {\n return obj.toISOString()\n }\n\n if (obj instanceof RegExp) {\n return obj.toString()\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => serializeWithDepth(item, depth + 1))\n }\n\n const result: Record<string, unknown> = {}\n seen.add(obj)\n\n for (const key of Object.keys(obj)) {\n const value = (obj as Record<string, unknown>)[key]\n if (typeof value === 'function') {\n result[key] = `[Function: ${value.name || 'anonymous'}]`\n } else if (typeof value === 'symbol') {\n result[key] = value.toString()\n } else if (value && typeof value === 'object' && seen.has(value as object)) {\n result[key] = '[Circular]'\n } else {\n result[key] = serializeWithDepth(value, depth + 1)\n }\n }\n\n return result\n }\n\n try {\n const depthLimited = serializeWithDepth(data, 0)\n const json = JSON.stringify(depthLimited, replacer, 0)\n\n if (json && json.length > maxLength) {\n return json.slice(0, maxLength) + 'โ€ฆ'\n }\n\n return json\n } catch {\n return '[Unserializable]'\n }\n}\n","const RESET = '\\x1b[0m'\nconst GREEN = '\\x1b[32m'\nconst RED = '\\x1b[31m'\nconst YELLOW = '\\x1b[33m'\nconst CYAN = '\\x1b[36m'\nconst MAGENTA = '\\x1b[35m'\nconst DIM = '\\x1b[2m'\n\n/** Check if NO_COLOR environment variable is set */\nfunction colorsEnabled(): boolean {\n if (typeof process !== 'undefined' && process.env?.NO_COLOR) {\n return false\n }\n return true\n}\n\nconst useColors = colorsEnabled()\n\n/** Apply green formatting */\nexport function green(s: string): string {\n return useColors ? `${GREEN}${s}${RESET}` : s\n}\n\n/** Apply red formatting */\nexport function red(s: string): string {\n return useColors ? `${RED}${s}${RESET}` : s\n}\n\n/** Apply yellow formatting */\nexport function yellow(s: string): string {\n return useColors ? `${YELLOW}${s}${RESET}` : s\n}\n\n/** Apply cyan formatting */\nexport function cyan(s: string): string {\n return useColors ? `${CYAN}${s}${RESET}` : s\n}\n\n/** Apply magenta formatting */\nexport function magenta(s: string): string {\n return useColors ? `${MAGENTA}${s}${RESET}` : s\n}\n\n/** Apply dim formatting */\nexport function dim(s: string): string {\n return useColors ? `${DIM}${s}${RESET}` : s\n}\n","import { FlowTrace, StepTrace } from '../core/types'\nimport { FlowRenderer } from './types'\nimport { green, red, cyan, dim } from './ansi'\nimport { safeSerialize } from '../adapter/safe-serializer'\n\n/** Format duration as human-readable string */\nfunction formatDuration(start: number, end?: number): string {\n if (end === undefined) return 'โ€”'\n const ms = Math.round(end - start)\n if (ms >= 1000) {\n return `${(ms / 1000).toFixed(1)}s`\n }\n return `${ms}ms`\n}\n\n/** Render a single step with indentation */\nfunction renderStep(\n step: Readonly<StepTrace>,\n indent: number,\n lines: string[]\n): void {\n const prefix = ' '.repeat(indent)\n\n // Step start\n lines.push(`${prefix}${cyan('โ†’')} ${step.name}`)\n\n // Inline data if present\n if (step.data !== undefined) {\n const serialized = safeSerialize(step.data, { maxLength: 80 })\n lines.push(`${prefix} ${dim('data:')} ${serialized}`)\n }\n\n // Step completion\n const duration = formatDuration(step.start, step.end)\n if (step.status === 'success') {\n lines.push(`${prefix}${green('โœ“')} ${step.name} (${duration})`)\n } else if (step.status === 'error') {\n const errorMsg = step.error?.message ?? 'Unknown error'\n lines.push(`${prefix}${red('โœ—')} ${step.name} (error: ${errorMsg})`)\n } else {\n lines.push(`${prefix} ${step.name} (${duration})`)\n }\n\n // Render children recursively\n for (const child of step.children) {\n renderStep(child, indent + 1, lines)\n }\n}\n\n/**\n * ConsoleRenderer โ€” deferred, atomic console output with ANSI colors.\n *\n * Produces entire output in a single console.log call on flow completion.\n */\nexport class ConsoleRenderer implements FlowRenderer {\n render(snapshot: Readonly<FlowTrace>): void {\n const lines: string[] = []\n\n // Flow header\n const tagsStr =\n snapshot.tags.length > 0\n ? ` [${snapshot.tags.join(', ')}]`\n : ''\n const headerIcon = snapshot.status === 'error' ? '๐Ÿ”ฅ' : '๐Ÿš€'\n lines.push(\n `${headerIcon} [flow:${snapshot.name}]${tagsStr} (${dim('tid:')} ${snapshot.id})`\n )\n\n // Steps\n for (const step of snapshot.steps) {\n renderStep(step, 1, lines)\n }\n\n // Flow completion\n const duration = formatDuration(snapshot.start, snapshot.end)\n if (snapshot.status === 'success') {\n let footer = `๐Ÿ [flow:${snapshot.name}] ${green('success')} (${duration})`\n if (snapshot.meta?.maxStepsExceeded) {\n footer += ` ${red('โš ๏ธ maxSteps exceeded')}`\n }\n lines.push(footer)\n } else if (snapshot.status === 'error') {\n const errorMsg = snapshot.error?.message ?? 'Unknown error'\n let footer = `๐Ÿ [flow:${snapshot.name}] ${red('failed')} (${duration}, error: ${errorMsg})`\n if (snapshot.meta?.maxStepsExceeded) {\n footer += ` ${red('โš ๏ธ maxSteps exceeded')}`\n }\n lines.push(footer)\n }\n\n // Atomic write โ€” single console call\n const output = lines.join('\\n')\n if (snapshot.status === 'error') {\n console.error(output)\n } else {\n console.log(output)\n }\n }\n}\n","export { FlowHandle } from './flow-handle'\nexport { StepHandle } from './step-handle'\nexport { RandomIdGenerator } from './id-generator'\nexport { DateClock } from './clock'\nexport { normalizeError } from './error-normalizer'\nexport { safeSerialize, SerializeOptions } from './safe-serializer'\nexport { NOOP_FLOW, NOOP_STEP, NoOpStepHandle, NoOpFlowHandle } from './noop-singletons'\n\nimport { FlowTrace, FlowOptions } from '../core/types'\nimport { FlowHandle, FlowHandleImpl } from './flow-handle'\nimport { IdGenerator, RandomIdGenerator } from './id-generator'\nimport { Clock, DateClock } from './clock'\nimport { ConsoleRenderer } from '../renderer/console'\nimport { NOOP_FLOW } from './noop-singletons'\nimport { subscribe } from './flow-handle'\n\n// Default instances\nconst _defaultIdGen = new RandomIdGenerator()\nconst _defaultClock = new DateClock()\nconst _defaultRenderer = new ConsoleRenderer()\n\n/**\n * Create a new execution flow with a human-readable name and optional configuration.\n *\n * @param name - Human-readable flow name\n * @param options - Optional configuration (enabled, tags, maxSteps)\n * @returns FlowHandle for interacting with the flow\n */\nexport function createFlow(name: string, options?: FlowOptions): FlowHandle {\n // Silent mode โ€” return NoOp singleton\n if (options?.enabled === false) {\n return NOOP_FLOW\n }\n\n const trace: FlowTrace = {\n id: _defaultIdGen.generate(),\n name,\n state: 'running',\n tags: options?.tags ?? [],\n start: _defaultClock.now(),\n context: {},\n steps: [],\n }\n\n return new FlowHandleImpl(\n trace,\n _defaultClock,\n _defaultIdGen,\n _defaultRenderer,\n options ?? {}\n )\n}\n\nexport { subscribe }\n","import { FlowTrace, StepTrace } from '../core/types'\nimport { FlowRenderer } from './types'\nimport { safeSerialize } from '../adapter/safe-serializer'\n\n/**\n * JSONRenderer โ€” portable FlowTrace serialization.\n *\n * Outputs versioned JSON structure suitable for downstream consumption.\n */\nexport class JSONRenderer implements FlowRenderer {\n render(snapshot: Readonly<FlowTrace>): void {\n const json = toJSON(snapshot)\n console.log(json)\n }\n}\n\n/** Check if input is a FlowHandle-like object with a .trace property */\nfunction hasTraceProperty(\n input: unknown\n): input is { trace: Readonly<FlowTrace> } {\n return (\n typeof input === 'object' &&\n input !== null &&\n 'trace' in input &&\n typeof (input as Record<string, unknown>).trace === 'object' &&\n (input as Record<string, unknown>).trace !== null\n )\n}\n\n/**\n * Serialize a FlowTrace to versioned JSON string.\n * Accepts either a FlowTrace directly or a FlowHandle (extracts .trace).\n * Omits undefined values for clean output.\n */\nexport function toJSON(input: Readonly<FlowTrace> | { trace: Readonly<FlowTrace> }): string {\n const trace: Readonly<FlowTrace> = hasTraceProperty(input) ? input.trace : input\n\n return JSON.stringify(\n {\n version: 1,\n flow: {\n id: trace.id,\n name: trace.name,\n ...(trace.parentId !== undefined && { parentId: trace.parentId }),\n state: trace.state,\n ...(trace.status !== undefined && { status: trace.status }),\n tags: trace.tags,\n start: trace.start,\n ...(trace.end !== undefined && { end: trace.end }),\n ...(trace.error !== undefined && { error: trace.error }),\n context: trace.context,\n steps: trace.steps.map(renderStep),\n ...(trace.meta !== undefined && { meta: trace.meta }),\n },\n },\n null,\n 2\n )\n}\n\nfunction renderStep(step: Readonly<StepTrace>): unknown {\n const obj: Record<string, unknown> = {\n id: step.id,\n name: step.name,\n sequence: step.sequence,\n state: step.state,\n status: step.status,\n start: step.start,\n }\n\n if (step.end !== undefined) obj.end = step.end\n if (step.data !== undefined) {\n obj.data = safeSerialize(step.data)\n }\n if (step.error !== undefined) obj.error = step.error\n if (step.children.length > 0) {\n obj.children = step.children.map(renderStep)\n }\n\n return obj\n}\n"],"mappings":"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","names":["normalizeError","input","StepHandleImpl","_trace","_clock","error","normalizeError","noop","NOOP_STEP","NOOP_FLOW","cloneDeep","obj","seen","clone","value","arr","item","result","key","deepFreeze","createSnapshot","trace","cloned","_listeners","subscribe","listener","_fireSubscribers","snapshot","FlowHandleImpl","_FlowHandleImpl","_trace","_clock","_idGen","_renderer","_options","name","data","NOOP_STEP","maxSteps","stepTrace","StepHandleImpl","NOOP_FLOW","childTrace","context","error","normalizeError","status","createSnapshot","RandomIdGenerator","DateClock","safeSerialize","data","options","maxDepth","maxLength","seen","replacer","key","value","serializeWithDepth","obj","depth","item","result","depthLimited","json","RESET","GREEN","RED","CYAN","DIM","colorsEnabled","useColors","green","s","GREEN","RESET","red","RED","cyan","s","useColors","CYAN","RESET","dim","s","useColors","DIM","RESET","formatDuration","start","end","ms","renderStep","step","indent","lines","prefix","cyan","serialized","safeSerialize","dim","duration","green","errorMsg","red","child","ConsoleRenderer","snapshot","tagsStr","headerIcon","footer","output","_defaultIdGen","RandomIdGenerator","_defaultClock","DateClock","_defaultRenderer","ConsoleRenderer","createFlow","name","options","NOOP_FLOW","trace","FlowHandleImpl","hasTraceProperty","input","toJSON","trace","renderStep","step","obj","safeSerialize"]}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@better-logger/core",
3
+ "version": "0.1.0",
4
+ "description": "Execution flow debugger for modern apps. Zero deps. Works everywhere. <10KB.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/0xmilord/better-logger"
9
+ },
10
+ "author": "0xmilord",
11
+ "type": "module",
12
+ "main": "./dist/index.cjs",
13
+ "module": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "import": {
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ },
21
+ "require": {
22
+ "types": "./dist/index.d.cts",
23
+ "default": "./dist/index.cjs"
24
+ }
25
+ }
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "engines": {
34
+ "node": ">=18"
35
+ },
36
+ "scripts": {
37
+ "lint": "eslint src/ --fix",
38
+ "test": "vitest run --coverage",
39
+ "build": "tsup src/index.ts --format esm,cjs --dts --minify",
40
+ "size": "cat dist/index.js | gzip | wc -c",
41
+ "changeset": "node scripts/new-changeset.cjs",
42
+ "release": "node scripts/release.cjs",
43
+ "release:dry-run": "node scripts/release.cjs --dry-run"
44
+ },
45
+ "dependencies": {},
46
+ "devDependencies": {
47
+ "typescript": "6.0.2",
48
+ "tsup": "8.5.1",
49
+ "vitest": "4.1.2",
50
+ "@vitest/coverage-v8": "4.1.2",
51
+ "eslint": "10.1.0",
52
+ "@typescript-eslint/parser": "8.58.0",
53
+ "@typescript-eslint/eslint-plugin": "8.58.0",
54
+ "@types/node": "20.11.0"
55
+ }
56
+ }