taist 0.1.1 → 0.1.3
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/instrument.js +20 -1
- package/lib/toon-formatter.js +23 -0
- package/lib/vitest-reporter.js +17 -2
- package/package.json +1 -1
- package/types/instrument.d.ts +14 -0
- package/types/trace-collector.d.ts +86 -0
package/instrument.js
CHANGED
|
@@ -49,6 +49,14 @@ import {
|
|
|
49
49
|
instrumentClassWithContext
|
|
50
50
|
} from './lib/instrument-all.js';
|
|
51
51
|
|
|
52
|
+
// Initialize global reporter for cross-process trace collection
|
|
53
|
+
// This must happen BEFORE any instrumentation so traces are sent to collector
|
|
54
|
+
const reporter = getGlobalReporter();
|
|
55
|
+
if (process.env.TAIST_COLLECTOR_SOCKET) {
|
|
56
|
+
logger.log(`Connecting to trace collector: ${process.env.TAIST_COLLECTOR_SOCKET}`);
|
|
57
|
+
reporter.connectEager();
|
|
58
|
+
}
|
|
59
|
+
|
|
52
60
|
// Initialize global tracer from environment variables
|
|
53
61
|
const tracer = new ServiceTracer({
|
|
54
62
|
enabled: process.env.TAIST_ENABLED !== 'false',
|
|
@@ -62,7 +70,18 @@ const tracer = new ServiceTracer({
|
|
|
62
70
|
});
|
|
63
71
|
|
|
64
72
|
// Export for manual instrumentation
|
|
65
|
-
export { tracer, autoInstrument };
|
|
73
|
+
export { tracer, autoInstrument, reporter };
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Flush any buffered traces to the collector.
|
|
77
|
+
* Call this before process exit if you want to ensure all traces are sent.
|
|
78
|
+
* @returns {Promise<void>}
|
|
79
|
+
*/
|
|
80
|
+
export async function flushTraces() {
|
|
81
|
+
if (reporter) {
|
|
82
|
+
await reporter.flush();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
66
85
|
|
|
67
86
|
// Log initialization
|
|
68
87
|
if (tracer.options.enabled) {
|
package/lib/toon-formatter.js
CHANGED
|
@@ -45,10 +45,22 @@ export class ToonFormatter {
|
|
|
45
45
|
*/
|
|
46
46
|
format(results) {
|
|
47
47
|
const lines = [];
|
|
48
|
+
const total = results.stats?.total || 0;
|
|
49
|
+
const tests = results.tests || [];
|
|
48
50
|
|
|
49
51
|
// Header
|
|
50
52
|
lines.push(this.formatHeader(results));
|
|
51
53
|
|
|
54
|
+
// Show passing tests when running small batches (≤10 tests)
|
|
55
|
+
if (total > 0 && total <= 10) {
|
|
56
|
+
const passingTests = tests.filter(t => t.state === 'pass');
|
|
57
|
+
for (const test of passingTests) {
|
|
58
|
+
const duration = Math.round(test.duration);
|
|
59
|
+
const shortName = this.shortenTestName(test.name);
|
|
60
|
+
lines.push(`✓ ${shortName} (${duration}ms)`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
52
64
|
// Failures
|
|
53
65
|
if (results.failures && results.failures.length > 0) {
|
|
54
66
|
lines.push('');
|
|
@@ -76,8 +88,19 @@ export class ToonFormatter {
|
|
|
76
88
|
return lines.join('\n');
|
|
77
89
|
}
|
|
78
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Shorten a test name for display
|
|
93
|
+
* "Suite > Nested > actual test name" -> "actual test name"
|
|
94
|
+
*/
|
|
95
|
+
shortenTestName(name) {
|
|
96
|
+
if (!name) return '';
|
|
97
|
+
const parts = name.split(' > ');
|
|
98
|
+
return parts[parts.length - 1] || name;
|
|
99
|
+
}
|
|
100
|
+
|
|
79
101
|
/**
|
|
80
102
|
* Format test result header
|
|
103
|
+
* Note: total represents tests that actually ran, not discovered
|
|
81
104
|
*/
|
|
82
105
|
formatHeader(results) {
|
|
83
106
|
const passed = results.stats?.passed || 0;
|
package/lib/vitest-reporter.js
CHANGED
|
@@ -59,6 +59,7 @@ export class TaistReporter {
|
|
|
59
59
|
this.taskResults = new Map(); // Map task id to result
|
|
60
60
|
this.results = {
|
|
61
61
|
stats: { total: 0, passed: 0, failed: 0, skipped: 0 },
|
|
62
|
+
tests: [], // Individual test results for enhanced output
|
|
62
63
|
failures: [],
|
|
63
64
|
duration: 0,
|
|
64
65
|
trace: []
|
|
@@ -117,6 +118,7 @@ export class TaistReporter {
|
|
|
117
118
|
|
|
118
119
|
// Reset stats
|
|
119
120
|
this.results.stats = { total: 0, passed: 0, failed: 0, skipped: 0 };
|
|
121
|
+
this.results.tests = [];
|
|
120
122
|
this.results.failures = [];
|
|
121
123
|
|
|
122
124
|
// Process all test files
|
|
@@ -181,17 +183,30 @@ export class TaistReporter {
|
|
|
181
183
|
*/
|
|
182
184
|
_processTask(task, file) {
|
|
183
185
|
if (task.type === 'test') {
|
|
184
|
-
this.results.stats.total++;
|
|
185
|
-
|
|
186
186
|
const state = task.result?.state;
|
|
187
|
+
|
|
188
|
+
// Only count tests that actually ran (have a pass/fail result)
|
|
187
189
|
if (state === 'pass') {
|
|
190
|
+
this.results.stats.total++;
|
|
188
191
|
this.results.stats.passed++;
|
|
192
|
+
this.results.tests.push({
|
|
193
|
+
name: this._getTestName(task),
|
|
194
|
+
duration: task.result?.duration || 0,
|
|
195
|
+
state: 'pass'
|
|
196
|
+
});
|
|
189
197
|
} else if (state === 'fail') {
|
|
198
|
+
this.results.stats.total++;
|
|
190
199
|
this.results.stats.failed++;
|
|
191
200
|
this.results.failures.push(this._formatFailure(task, file));
|
|
201
|
+
this.results.tests.push({
|
|
202
|
+
name: this._getTestName(task),
|
|
203
|
+
duration: task.result?.duration || 0,
|
|
204
|
+
state: 'fail'
|
|
205
|
+
});
|
|
192
206
|
} else if (state === 'skip') {
|
|
193
207
|
this.results.stats.skipped++;
|
|
194
208
|
}
|
|
209
|
+
// Tests without state (filtered out) are not counted
|
|
195
210
|
} else if (task.type === 'suite' && task.tasks) {
|
|
196
211
|
// Process nested tasks
|
|
197
212
|
for (const subtask of task.tasks) {
|
package/package.json
CHANGED
package/types/instrument.d.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import type { Application as ExpressApp } from 'express';
|
|
16
|
+
import type { TraceReporter } from './trace-collector';
|
|
16
17
|
|
|
17
18
|
export interface TraceContext {
|
|
18
19
|
/** Call depth (0 = root) */
|
|
@@ -85,6 +86,19 @@ export declare class ServiceTracer {
|
|
|
85
86
|
*/
|
|
86
87
|
export declare const tracer: ServiceTracer;
|
|
87
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Global reporter instance for cross-process trace collection.
|
|
91
|
+
* Automatically connects to TAIST_COLLECTOR_SOCKET if set.
|
|
92
|
+
*/
|
|
93
|
+
export declare const reporter: TraceReporter;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Flush any buffered traces to the collector.
|
|
97
|
+
* Call this before process exit to ensure all traces are sent.
|
|
98
|
+
* @returns Promise that resolves when flush completes
|
|
99
|
+
*/
|
|
100
|
+
export declare function flushTraces(): Promise<void>;
|
|
101
|
+
|
|
88
102
|
/**
|
|
89
103
|
* Auto-instrument a module's exports
|
|
90
104
|
* @param moduleExports Module exports object
|
|
@@ -126,3 +126,89 @@ export declare function createDefaultFilter(
|
|
|
126
126
|
): (trace: TraceObject) => boolean;
|
|
127
127
|
|
|
128
128
|
export default TraceCollector;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* TraceReporter Options
|
|
132
|
+
*/
|
|
133
|
+
export interface TraceReporterOptions {
|
|
134
|
+
/** Socket path to connect to (defaults to TAIST_COLLECTOR_SOCKET env var) */
|
|
135
|
+
socketPath?: string;
|
|
136
|
+
/** Number of traces to buffer before auto-flush (default: 100) */
|
|
137
|
+
batchSize?: number;
|
|
138
|
+
/** Flush interval in ms (default: 1000) */
|
|
139
|
+
flushInterval?: number;
|
|
140
|
+
/** Max connection retries (default: 3) */
|
|
141
|
+
maxRetries?: number;
|
|
142
|
+
/** Retry delay in ms (default: 100) */
|
|
143
|
+
retryDelay?: number;
|
|
144
|
+
/** Worker ID for identifying trace source */
|
|
145
|
+
workerId?: string | number;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* TraceReporter - Client that runs in worker processes to send traces to the collector.
|
|
150
|
+
*
|
|
151
|
+
* Features:
|
|
152
|
+
* - Connects to collector via Unix domain socket
|
|
153
|
+
* - Buffers traces locally for batched sending
|
|
154
|
+
* - Auto-flushes on process exit
|
|
155
|
+
* - Handles connection failures gracefully
|
|
156
|
+
*/
|
|
157
|
+
export declare class TraceReporter extends EventEmitter {
|
|
158
|
+
constructor(options?: TraceReporterOptions);
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Connect to the collector socket
|
|
162
|
+
*/
|
|
163
|
+
connect(): Promise<void>;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Start connection eagerly (call at module init time)
|
|
167
|
+
*/
|
|
168
|
+
connectEager(): this;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Report a single trace event
|
|
172
|
+
*/
|
|
173
|
+
report(trace: TraceObject): void;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Async flush - sends buffered traces to collector
|
|
177
|
+
*/
|
|
178
|
+
flush(): Promise<void>;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Synchronous flush for process exit - best effort
|
|
182
|
+
*/
|
|
183
|
+
flushSync(): void;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Close the reporter connection
|
|
187
|
+
*/
|
|
188
|
+
close(): void;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Check if connected to collector
|
|
192
|
+
*/
|
|
193
|
+
isConnected(): boolean;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Get current buffer size
|
|
197
|
+
*/
|
|
198
|
+
getBufferSize(): number;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Get or create the global reporter instance
|
|
203
|
+
*/
|
|
204
|
+
export declare function getGlobalReporter(options?: TraceReporterOptions): TraceReporter;
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Report a trace using the global reporter
|
|
208
|
+
*/
|
|
209
|
+
export declare function report(trace: TraceObject): void;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Flush the global reporter
|
|
213
|
+
*/
|
|
214
|
+
export declare function flush(): Promise<void>;
|