taist 0.2.10 → 0.2.12

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.
@@ -72,9 +72,15 @@ export class TraceCollector extends EventEmitter {
72
72
  _handleConnection(socket) {
73
73
  this.connections.add(socket);
74
74
  let buffer = "";
75
+ const lifecycleDebug = process.env.TAIST_TRACE_LIFECYCLE === 'true';
75
76
 
76
77
  socket.on("data", (chunk) => {
77
- buffer += chunk.toString();
78
+ const data = chunk.toString();
79
+ buffer += data;
80
+
81
+ if (lifecycleDebug) {
82
+ console.log(`[LIFECYCLE collector] [${Date.now()}] RAW DATA received, length:`, data.length, 'buffer now:', buffer.length);
83
+ }
78
84
 
79
85
  // Process complete NDJSON lines
80
86
  const lines = buffer.split("\n");
@@ -88,6 +94,9 @@ export class TraceCollector extends EventEmitter {
88
94
  });
89
95
 
90
96
  socket.on("close", () => {
97
+ if (lifecycleDebug) {
98
+ console.log('[LIFECYCLE collector] Socket closed, remaining buffer:', buffer.length, 'chars');
99
+ }
91
100
  // Process any remaining data in buffer
92
101
  if (buffer.trim()) {
93
102
  this._processMessage(buffer);
@@ -96,18 +105,26 @@ export class TraceCollector extends EventEmitter {
96
105
  });
97
106
 
98
107
  socket.on("error", (err) => {
108
+ if (lifecycleDebug) {
109
+ console.log('[LIFECYCLE collector] Socket error:', err.message);
110
+ }
99
111
  this.emit("connectionError", err);
100
112
  this.connections.delete(socket);
101
113
  });
102
114
  }
103
115
 
104
116
  _processMessage(line) {
117
+ const lifecycleDebug = process.env.TAIST_TRACE_LIFECYCLE === 'true';
118
+
105
119
  try {
106
120
  const message = JSON.parse(line);
107
121
 
108
122
  if (message.type === "trace") {
109
123
  this._addTrace(message.data);
110
124
  } else if (message.type === "batch") {
125
+ if (lifecycleDebug) {
126
+ console.log('[LIFECYCLE collector] Processing batch of', message.data?.length, 'traces');
127
+ }
111
128
  for (const trace of message.data) {
112
129
  this._addTrace(trace);
113
130
  }
@@ -115,6 +132,9 @@ export class TraceCollector extends EventEmitter {
115
132
  this.emit("flush", { workerId: message.workerId });
116
133
  }
117
134
  } catch (err) {
135
+ if (lifecycleDebug) {
136
+ console.log('[LIFECYCLE collector] PARSE ERROR:', err.message, 'line:', line.slice(0, 100));
137
+ }
118
138
  this.emit("parseError", { error: err, line });
119
139
  }
120
140
  }
@@ -143,7 +163,7 @@ export class TraceCollector extends EventEmitter {
143
163
  }
144
164
 
145
165
  if (lifecycleDebug) {
146
- console.log('[LIFECYCLE collector] RECEIVED:', trace.name, 'depth:', trace.depth, 'correlationId:', trace.correlationId);
166
+ console.log(`[LIFECYCLE collector] [${Date.now()}] RECEIVED:`, trace.name, 'depth:', trace.depth, 'correlationId:', trace.correlationId);
147
167
  } else if (debug) {
148
168
  console.log('[collector] RECEIVED:', trace.name, 'depth:', trace.depth, 'correlationId:', trace.correlationId);
149
169
  }
@@ -182,6 +202,12 @@ export class TraceCollector extends EventEmitter {
182
202
  return;
183
203
  }
184
204
 
205
+ const lifecycleDebug = process.env.TAIST_TRACE_LIFECYCLE === 'true';
206
+
207
+ if (lifecycleDebug) {
208
+ console.log(`[LIFECYCLE collector] [${Date.now()}] stop() called, connections:`, this.connections.size);
209
+ }
210
+
185
211
  // Send shutdown signal to all connected workers
186
212
  const shutdownMessage = JSON.stringify({ type: 'shutdown' }) + '\n';
187
213
  for (const socket of this.connections) {
@@ -198,45 +224,87 @@ export class TraceCollector extends EventEmitter {
198
224
  await new Promise(resolve => setTimeout(resolve, 50));
199
225
  }
200
226
 
227
+ if (lifecycleDebug) {
228
+ console.log('[LIFECYCLE collector] After wait, remaining connections:', this.connections.size);
229
+ }
230
+
201
231
  return new Promise((resolve) => {
202
- // Gracefully close any remaining connections (allows pending data to be read)
203
- for (const socket of this.connections) {
232
+ // If all connections closed gracefully, we're done
233
+ if (this.connections.size === 0) {
234
+ this._finishStop(resolve);
235
+ return;
236
+ }
237
+
238
+ // Track sockets that need closing
239
+ const socketsToClose = new Set(this.connections);
240
+ let closedCount = 0;
241
+
242
+ const onSocketClosed = (socket) => {
243
+ socketsToClose.delete(socket);
244
+ closedCount++;
245
+ if (lifecycleDebug) {
246
+ console.log('[LIFECYCLE collector] Socket closed during shutdown, remaining:', socketsToClose.size);
247
+ }
248
+ if (socketsToClose.size === 0) {
249
+ clearTimeout(forceCloseTimeout);
250
+ this._finishStop(resolve);
251
+ }
252
+ };
253
+
254
+ // Set up close handlers for remaining sockets
255
+ for (const socket of socketsToClose) {
256
+ socket.once('close', () => onSocketClosed(socket));
204
257
  try {
258
+ // socket.end() sends FIN but allows pending data to be read
205
259
  socket.end();
206
260
  } catch {
207
- try { socket.destroy(); } catch { /* ignore */ }
261
+ // Socket already closed
262
+ onSocketClosed(socket);
208
263
  }
209
264
  }
210
265
 
211
- // Give a moment for final data to arrive before destroying
212
- setTimeout(() => {
213
- // Force destroy any sockets that didn't close gracefully
214
- for (const socket of this.connections) {
266
+ // Force close after extended grace period (500ms instead of 100ms)
267
+ // This gives more time for in-flight data to be received
268
+ const forceCloseTimeout = setTimeout(() => {
269
+ if (lifecycleDebug) {
270
+ console.log('[LIFECYCLE collector] Force closing', socketsToClose.size, 'remaining sockets');
271
+ }
272
+ for (const socket of socketsToClose) {
215
273
  try {
216
274
  socket.destroy();
217
275
  } catch {
218
276
  // Ignore
219
277
  }
220
278
  }
221
- this.connections.clear();
222
-
223
- // Close server
224
- this.server.close(() => {
225
- this.started = false;
226
-
227
- // Clean up socket file
228
- if (process.platform !== "win32") {
229
- try {
230
- fs.unlinkSync(this.socketPath);
231
- } catch {
232
- // Ignore
233
- }
234
- }
279
+ this._finishStop(resolve);
280
+ }, 500);
281
+ });
282
+ }
283
+
284
+ _finishStop(resolve) {
285
+ const lifecycleDebug = process.env.TAIST_TRACE_LIFECYCLE === 'true';
286
+
287
+ this.connections.clear();
288
+
289
+ // Close server
290
+ this.server.close(() => {
291
+ this.started = false;
292
+
293
+ // Clean up socket file
294
+ if (process.platform !== "win32") {
295
+ try {
296
+ fs.unlinkSync(this.socketPath);
297
+ } catch {
298
+ // Ignore
299
+ }
300
+ }
301
+
302
+ if (lifecycleDebug) {
303
+ console.log('[LIFECYCLE collector] Stopped, total traces collected:', this.traces.length);
304
+ }
235
305
 
236
- this.emit("stopped");
237
- resolve();
238
- });
239
- }, 100); // 100ms grace period for data to be read
306
+ this.emit("stopped");
307
+ resolve();
240
308
  });
241
309
  }
242
310
 
@@ -317,10 +317,16 @@ export class TraceReporter extends EventEmitter {
317
317
  const traces = this.buffer.splice(0, this.buffer.length);
318
318
 
319
319
  if (lifecycleDebug) {
320
- console.log('[LIFECYCLE reporter] SENDING', traces.length, 'traces to collector');
320
+ const now = Date.now();
321
+ console.log(`[LIFECYCLE reporter] [${now}] SENDING`, traces.length, 'traces to collector');
321
322
  for (const t of traces) {
322
323
  console.log('[LIFECYCLE reporter] -', t.name, 'depth:', t.depth);
323
324
  }
325
+ // Log socket state to diagnose connection issues
326
+ console.log(`[LIFECYCLE reporter] [${now}] Socket state: destroyed=`, this.socket.destroyed,
327
+ 'writableEnded=', this.socket.writableEnded,
328
+ 'readableEnded=', this.socket.readableEnded,
329
+ 'pending=', this.socket.pending);
324
330
  }
325
331
 
326
332
  const message = JSON.stringify({
@@ -330,17 +336,21 @@ export class TraceReporter extends EventEmitter {
330
336
  });
331
337
 
332
338
  return new Promise((resolve, reject) => {
333
- this.socket.write(message + "\n", (err) => {
339
+ const writeResult = this.socket.write(message + "\n", (err) => {
334
340
  if (err) {
335
- if (lifecycleDebug) console.log('[LIFECYCLE reporter] SEND FAILED:', err.message);
341
+ if (lifecycleDebug) console.log('[LIFECYCLE reporter] WRITE CALLBACK error:', err.message);
336
342
  // Put traces back in buffer on failure
337
343
  this.buffer.unshift(...traces);
338
344
  reject(err);
339
345
  } else {
346
+ if (lifecycleDebug) console.log('[LIFECYCLE reporter] WRITE CALLBACK success for', traces.length, 'traces');
340
347
  this.emit("flushed", { count: traces.length });
341
348
  resolve();
342
349
  }
343
350
  });
351
+ if (lifecycleDebug) {
352
+ console.log('[LIFECYCLE reporter] socket.write() returned:', writeResult, '(true=flushed to kernel, false=buffered)');
353
+ }
344
354
  });
345
355
  }
346
356
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taist",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "description": "Token-Optimized Testing Framework for AI-Assisted Development",
5
5
  "main": "index.js",
6
6
  "type": "module",