taist 0.2.8 → 0.2.10

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.
@@ -392,16 +392,20 @@ export class ToonFormatter {
392
392
  const maxGroups = options.maxGroups ?? 10;
393
393
  const showHeader = options.showHeader !== false;
394
394
  const debug = process.env.TAIST_DEBUG === 'true';
395
+ const lifecycleDebug = process.env.TAIST_TRACE_LIFECYCLE === 'true';
395
396
  const lines = [];
396
397
 
397
398
  if (!traces || traces.length === 0) {
398
399
  return 'No traces collected';
399
400
  }
400
401
 
401
- if (debug) {
402
- console.log('[formatter] Total traces:', traces.length);
403
- for (const t of traces) {
404
- console.log('[formatter] trace:', t.name, 'depth:', t.depth, 'correlationId:', t.correlationId);
402
+ if (debug || lifecycleDebug) {
403
+ console.log('[LIFECYCLE formatter] Total traces:', traces.length);
404
+ // Show all depth 0-2 traces (likely the missing resolver and intermediate calls)
405
+ const lowDepthTraces = traces.filter(t => t.depth <= 2);
406
+ console.log('[LIFECYCLE formatter] Traces with depth 0-2:', lowDepthTraces.length);
407
+ for (const t of lowDepthTraces) {
408
+ console.log('[LIFECYCLE formatter] depth', t.depth, ':', t.name, 'correlationId:', t.correlationId);
405
409
  }
406
410
  }
407
411
 
@@ -411,12 +415,12 @@ export class ToonFormatter {
411
415
  // Group by traceId
412
416
  const groups = this.groupTracesByRequest(sorted);
413
417
 
414
- if (debug) {
415
- console.log('[formatter] Groups:', groups.size);
418
+ if (debug || lifecycleDebug) {
419
+ console.log('[LIFECYCLE formatter] Groups:', groups.size);
416
420
  for (const [id, groupTraces] of groups) {
417
- console.log('[formatter] Group', id, ':', groupTraces.length, 'traces');
418
421
  const root = groupTraces.find(t => t.depth === 0);
419
- console.log('[formatter] root:', root?.name || 'none (using "Request")');
422
+ const minDepth = Math.min(...groupTraces.map(t => t.depth));
423
+ console.log('[LIFECYCLE formatter] Group', id, ':', groupTraces.length, 'traces, minDepth:', minDepth, 'root:', root?.name || 'none');
420
424
  }
421
425
  }
422
426
 
@@ -121,27 +121,30 @@ export class TraceCollector extends EventEmitter {
121
121
 
122
122
  _addTrace(trace) {
123
123
  const debug = process.env.TAIST_DEBUG === 'true';
124
+ const lifecycleDebug = process.env.TAIST_TRACE_LIFECYCLE === 'true';
124
125
 
125
126
  // Generate trace ID for deduplication
126
127
  const traceId =
127
128
  trace.id || `${trace.name}-${trace.timestamp}-${trace.type}`;
128
129
 
129
130
  if (this.traceIds.has(traceId)) {
130
- if (debug) {
131
- console.log('[collector] DUPLICATE:', trace.name, 'id:', traceId);
131
+ if (debug || lifecycleDebug) {
132
+ console.log('[LIFECYCLE collector] DUPLICATE:', trace.name, 'id:', traceId);
132
133
  }
133
134
  return; // Duplicate
134
135
  }
135
136
 
136
137
  // Apply filter
137
138
  if (!this.filter(trace)) {
138
- if (debug) {
139
- console.log('[collector] FILTERED:', trace.name);
139
+ if (debug || lifecycleDebug) {
140
+ console.log('[LIFECYCLE collector] FILTERED:', trace.name);
140
141
  }
141
142
  return; // Filtered out
142
143
  }
143
144
 
144
- if (debug) {
145
+ if (lifecycleDebug) {
146
+ console.log('[LIFECYCLE collector] RECEIVED:', trace.name, 'depth:', trace.depth, 'correlationId:', trace.correlationId);
147
+ } else if (debug) {
145
148
  console.log('[collector] RECEIVED:', trace.name, 'depth:', trace.depth, 'correlationId:', trace.correlationId);
146
149
  }
147
150
 
@@ -245,7 +245,13 @@ export class TraceReporter extends EventEmitter {
245
245
  * Report a single trace event
246
246
  */
247
247
  report(trace) {
248
- if (this.closed) return;
248
+ // Use TAIST_TRACE_LIFECYCLE for detailed lifecycle logging (always via console.log)
249
+ const lifecycleDebug = process.env.TAIST_TRACE_LIFECYCLE === 'true';
250
+
251
+ if (this.closed) {
252
+ if (lifecycleDebug) console.log('[LIFECYCLE reporter] REJECTED (closed):', trace.name);
253
+ return;
254
+ }
249
255
 
250
256
  // Update socketPath from env if not set (env may be set after construction)
251
257
  // This handles the case where vitest-reporter sets TAIST_COLLECTOR_SOCKET
@@ -256,6 +262,10 @@ export class TraceReporter extends EventEmitter {
256
262
  this._setupExitHandlers();
257
263
  }
258
264
 
265
+ if (lifecycleDebug) {
266
+ console.log('[LIFECYCLE reporter] BUFFERED:', trace.name, 'depth:', trace.depth, 'correlationId:', trace.correlationId, 'socketPath:', this.socketPath, 'connected:', this.connected);
267
+ }
268
+
259
269
  logger.debug("[reporter] report() called:", trace.name, trace.type);
260
270
 
261
271
  this.buffer.push(trace);
@@ -280,26 +290,39 @@ export class TraceReporter extends EventEmitter {
280
290
  * Async flush - sends buffered traces to collector
281
291
  */
282
292
  async flush() {
293
+ const lifecycleDebug = process.env.TAIST_TRACE_LIFECYCLE === 'true';
294
+
283
295
  if (this.buffer.length === 0 || this.closed) {
296
+ if (lifecycleDebug && this.closed) console.log('[LIFECYCLE reporter] flush() skipped - reporter closed');
284
297
  return;
285
298
  }
286
299
 
287
300
  // Ensure connected
288
301
  if (!this.connected && this.socketPath) {
289
302
  try {
303
+ if (lifecycleDebug) console.log('[LIFECYCLE reporter] flush() connecting to:', this.socketPath);
290
304
  await this.connect();
291
- } catch {
305
+ } catch (e) {
306
+ if (lifecycleDebug) console.log('[LIFECYCLE reporter] flush() connect failed:', e.message);
292
307
  // Connection failed, keep traces buffered
293
308
  return;
294
309
  }
295
310
  }
296
311
 
297
312
  if (!this.connected) {
313
+ if (lifecycleDebug) console.log('[LIFECYCLE reporter] flush() skipped - not connected, no socketPath');
298
314
  return;
299
315
  }
300
316
 
301
317
  const traces = this.buffer.splice(0, this.buffer.length);
302
318
 
319
+ if (lifecycleDebug) {
320
+ console.log('[LIFECYCLE reporter] SENDING', traces.length, 'traces to collector');
321
+ for (const t of traces) {
322
+ console.log('[LIFECYCLE reporter] -', t.name, 'depth:', t.depth);
323
+ }
324
+ }
325
+
303
326
  const message = JSON.stringify({
304
327
  type: "batch",
305
328
  workerId: this.workerId,
@@ -309,6 +332,7 @@ export class TraceReporter extends EventEmitter {
309
332
  return new Promise((resolve, reject) => {
310
333
  this.socket.write(message + "\n", (err) => {
311
334
  if (err) {
335
+ if (lifecycleDebug) console.log('[LIFECYCLE reporter] SEND FAILED:', err.message);
312
336
  // Put traces back in buffer on failure
313
337
  this.buffer.unshift(...traces);
314
338
  reject(err);
package/lib/transform.js CHANGED
@@ -317,6 +317,27 @@ const __taist_module = "${moduleName}";
317
317
  // This avoids TDZ issues with circular dependencies
318
318
  // We'll add __taist_instrumentClass() calls at the end instead of re-exporting
319
319
 
320
+ // For OBJECTS: Rename and re-export wrapped version (BUILD-TIME instrumentation)
321
+ // This is critical for GraphQL resolvers and similar nested object patterns.
322
+ // Runtime instrumentation doesn't work because bundlers capture the original reference.
323
+ for (const exp of objectExports) {
324
+ if (exp.declaration === "inline") {
325
+ // Inline exports: export const resolver = { ... }
326
+ // Rename: export const resolver -> const __taist_orig_resolver
327
+ transformed = transformed.replace(
328
+ new RegExp(`export\\s+const\\s+${exp.name}\\s*=\\s*\\{`, "g"),
329
+ `const __taist_orig_${exp.name} = {`
330
+ );
331
+ } else if (exp.declaration === "named") {
332
+ // Named exports: const resolver = { ... }; export { resolver }
333
+ // Rename: const resolver -> const __taist_orig_resolver
334
+ transformed = transformed.replace(
335
+ new RegExp(`\\b(const|let|var)\\s+${exp.name}\\s*=\\s*\\{`, "g"),
336
+ `$1 __taist_orig_${exp.name} = {`
337
+ );
338
+ }
339
+ }
340
+
320
341
  // For FUNCTIONS: Rename and re-export (original behavior)
321
342
  for (const exp of functionExports) {
322
343
  if (exp.declaration === "inline") {
@@ -354,10 +375,12 @@ const __taist_module = "${moduleName}";
354
375
  }
355
376
  }
356
377
 
357
- // Remove named export statements for FUNCTIONS we're replacing (not classes)
378
+ // Remove named export statements for FUNCTIONS and OBJECTS we're replacing (not classes)
358
379
  // Match: export { Name1, Name2 } and remove names we're wrapping
359
380
  const namedFunctionExportNames = functionExports.filter(e => e.declaration === "named").map(e => e.name);
360
- if (namedFunctionExportNames.length > 0) {
381
+ const namedObjectExportNames = objectExports.filter(e => e.declaration === "named").map(e => e.name);
382
+ const allNamedExportsToReplace = [...namedFunctionExportNames, ...namedObjectExportNames];
383
+ if (allNamedExportsToReplace.length > 0) {
361
384
  transformed = transformed.replace(
362
385
  /export\s*\{([^}]+)\}/g,
363
386
  (match, names) => {
@@ -365,7 +388,7 @@ const __taist_module = "${moduleName}";
365
388
  .map(n => n.trim())
366
389
  .filter(n => {
367
390
  const name = n.split(/\s+as\s+/)[0].trim();
368
- return !namedFunctionExportNames.includes(name);
391
+ return !allNamedExportsToReplace.includes(name);
369
392
  });
370
393
  if (remaining.length === 0) {
371
394
  return '// export moved to end';
@@ -418,12 +441,13 @@ const __taist_module = "${moduleName}";
418
441
  })
419
442
  .join("\n");
420
443
 
421
- // Add in-place instrumentation for OBJECT LITERALS (wraps nested methods)
422
- // __taist_instrumentObject recursively wraps function properties
423
- const objectInstrumentations = objectExports
444
+ // Add wrapped re-exports for OBJECTS at the end (BUILD-TIME instrumentation)
445
+ // This wraps nested methods like GraphQL resolvers: resolver.Mutation.upsertOrder
446
+ // Runtime instrumentation doesn't work because bundlers capture original references
447
+ const objectReexports = objectExports
424
448
  .map((exp) => {
425
449
  const nameExpr = `(__taist_module === "${exp.name}" ? "${exp.name}" : __taist_module + ".${exp.name}")`;
426
- return `__taist_instrumentObject(${exp.name}, ${nameExpr});`;
450
+ return `export const ${exp.name} = __taist_instrumentObject(__taist_orig_${exp.name}, ${nameExpr});`;
427
451
  })
428
452
  .join("\n");
429
453
 
@@ -433,12 +457,12 @@ const __taist_module = "${moduleName}";
433
457
  transformed += `// Wrapped function exports\n${functionReexports}\n`;
434
458
  }
435
459
 
436
- if (classInstrumentations) {
437
- transformed += `// In-place class instrumentation (preserves hoisting)\n${classInstrumentations}\n`;
460
+ if (objectReexports) {
461
+ transformed += `// Wrapped object exports (build-time instrumentation for nested methods)\n${objectReexports}\n`;
438
462
  }
439
463
 
440
- if (objectInstrumentations) {
441
- transformed += `// In-place object literal instrumentation (wraps nested methods)\n${objectInstrumentations}\n`;
464
+ if (classInstrumentations) {
465
+ transformed += `// In-place class instrumentation (preserves hoisting)\n${classInstrumentations}\n`;
442
466
  }
443
467
 
444
468
  // Add default exports at the end (after the wrapped versions are defined)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taist",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "description": "Token-Optimized Testing Framework for AI-Assisted Development",
5
5
  "main": "index.js",
6
6
  "type": "module",