taist 0.1.5 → 0.1.6

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.
@@ -184,28 +184,44 @@ export class TraceCollector extends EventEmitter {
184
184
  }
185
185
 
186
186
  return new Promise((resolve) => {
187
- // Force close any remaining connections
187
+ // Gracefully close any remaining connections (allows pending data to be read)
188
188
  for (const socket of this.connections) {
189
- socket.destroy();
189
+ try {
190
+ socket.end();
191
+ } catch {
192
+ try { socket.destroy(); } catch { /* ignore */ }
193
+ }
190
194
  }
191
- this.connections.clear();
192
-
193
- // Close server
194
- this.server.close(() => {
195
- this.started = false;
196
195
 
197
- // Clean up socket file
198
- if (process.platform !== "win32") {
196
+ // Give a moment for final data to arrive before destroying
197
+ setTimeout(() => {
198
+ // Force destroy any sockets that didn't close gracefully
199
+ for (const socket of this.connections) {
199
200
  try {
200
- fs.unlinkSync(this.socketPath);
201
+ socket.destroy();
201
202
  } catch {
202
203
  // Ignore
203
204
  }
204
205
  }
206
+ this.connections.clear();
207
+
208
+ // Close server
209
+ this.server.close(() => {
210
+ this.started = false;
211
+
212
+ // Clean up socket file
213
+ if (process.platform !== "win32") {
214
+ try {
215
+ fs.unlinkSync(this.socketPath);
216
+ } catch {
217
+ // Ignore
218
+ }
219
+ }
205
220
 
206
- this.emit("stopped");
207
- resolve();
208
- });
221
+ this.emit("stopped");
222
+ resolve();
223
+ });
224
+ }, 100); // 100ms grace period for data to be read
209
225
  });
210
226
  }
211
227
 
@@ -152,12 +152,14 @@ export class TraceReporter extends EventEmitter {
152
152
 
153
153
  /**
154
154
  * Handle shutdown signal from collector.
155
- * Immediately flushes buffer and closes connection.
155
+ * Flushes buffer and waits for data to be sent before closing.
156
156
  */
157
157
  _handleShutdown() {
158
158
  if (this.closed) return;
159
159
 
160
- // Flush synchronously and close
160
+ this._stopFlushTimer();
161
+
162
+ // Flush and wait for data to be sent
161
163
  if (this.buffer.length > 0 && this.socket && this.connected) {
162
164
  const traces = this.buffer.splice(0, this.buffer.length);
163
165
  const message = JSON.stringify({
@@ -166,15 +168,63 @@ export class TraceReporter extends EventEmitter {
166
168
  data: traces,
167
169
  });
168
170
 
171
+ logger.debug("[reporter] Shutdown flushing", traces.length, "traces");
172
+
169
173
  try {
170
- this.socket.write(message + "\n", () => {
171
- logger.debug("[reporter] Shutdown flush complete, closing");
172
- this.close();
173
- });
174
- } catch {
174
+ // Write data
175
+ const flushed = this.socket.write(message + "\n");
176
+
177
+ if (flushed) {
178
+ // Data was written to kernel buffer, use setImmediate to allow
179
+ // the event loop to process and send the data before closing
180
+ setImmediate(() => {
181
+ this._gracefulClose();
182
+ });
183
+ } else {
184
+ // Buffer is full, wait for drain
185
+ this.socket.once('drain', () => {
186
+ logger.debug("[reporter] Drained, closing");
187
+ this._gracefulClose();
188
+ });
189
+ }
190
+ } catch (err) {
191
+ logger.debug("[reporter] Write error:", err.message);
175
192
  this.close();
176
193
  }
177
194
  } else {
195
+ this._gracefulClose();
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Close connection gracefully, allowing pending data to be sent.
201
+ */
202
+ _gracefulClose() {
203
+ if (!this.socket) {
204
+ this.close();
205
+ return;
206
+ }
207
+
208
+ // Use socket.end() for graceful TCP close (sends FIN, allows pending data)
209
+ // Add a small delay to allow the collector to read the data
210
+ this.socket.once('close', () => {
211
+ logger.debug("[reporter] Socket closed gracefully");
212
+ this.closed = true;
213
+ this.connected = false;
214
+ this.socket = null;
215
+ });
216
+
217
+ // Set a timeout in case close doesn't happen
218
+ const closeTimeout = setTimeout(() => {
219
+ logger.debug("[reporter] Close timeout, forcing");
220
+ this.close();
221
+ }, 500);
222
+ closeTimeout.unref();
223
+
224
+ try {
225
+ this.socket.end();
226
+ } catch {
227
+ clearTimeout(closeTimeout);
178
228
  this.close();
179
229
  }
180
230
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taist",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Token-Optimized Testing Framework for AI-Assisted Development",
5
5
  "main": "index.js",
6
6
  "type": "module",