repro-nest 0.0.199 → 0.0.201

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.
Files changed (3) hide show
  1. package/dist/index.js +28 -12
  2. package/package.json +1 -1
  3. package/src/index.ts +29 -12
package/dist/index.js CHANGED
@@ -1148,12 +1148,15 @@ function reproMiddleware(cfg) {
1148
1148
  let unsubscribe;
1149
1149
  let flushed = false;
1150
1150
  let finished = false;
1151
+ let finishedAt = null;
1152
+ let lastEventAt = Date.now();
1151
1153
  let idleTimer = null;
1152
1154
  let hardStopTimer = null;
1153
1155
  let drainTimer = null;
1154
1156
  let flushPayload = null;
1155
1157
  const activeSpans = new Set();
1156
1158
  let anonymousSpanDepth = 0;
1159
+ const ACTIVE_SPAN_FORCE_FLUSH_MS = 30000; // safety guard against leaks
1157
1160
  const clearTimers = () => {
1158
1161
  if (idleTimer) {
1159
1162
  try {
@@ -1178,7 +1181,7 @@ function reproMiddleware(cfg) {
1178
1181
  }
1179
1182
  };
1180
1183
  const hasActiveWork = () => activeSpans.size > 0 || anonymousSpanDepth > 0;
1181
- const scheduleIdleFlush = () => {
1184
+ const scheduleIdleFlush = (delay = TRACE_IDLE_FLUSH_MS) => {
1182
1185
  if (!finished || flushed)
1183
1186
  return;
1184
1187
  if (hasActiveWork())
@@ -1189,15 +1192,30 @@ function reproMiddleware(cfg) {
1189
1192
  }
1190
1193
  catch { }
1191
1194
  }
1192
- idleTimer = setTimeout(() => doFlush(false), TRACE_IDLE_FLUSH_MS);
1195
+ idleTimer = setTimeout(() => doFlush(false), delay);
1193
1196
  };
1194
1197
  const doFlush = (force = false) => {
1195
1198
  if (flushed)
1196
1199
  return;
1197
- if (!force && hasActiveWork()) {
1198
- scheduleIdleFlush();
1200
+ const now = Date.now();
1201
+ const stillActive = hasActiveWork();
1202
+ const quietMs = now - lastEventAt;
1203
+ const waitedFinish = finishedAt === null ? 0 : now - finishedAt;
1204
+ // If work is still active and we haven't been quiet long enough, defer.
1205
+ if (stillActive && !force) {
1206
+ const remaining = Math.max(0, TRACE_LINGER_AFTER_FINISH_MS - quietMs);
1207
+ scheduleIdleFlush(Math.max(remaining, 10));
1199
1208
  return;
1200
1209
  }
1210
+ if (stillActive && force) {
1211
+ // Allow forced flush after either linger window of silence or max guard.
1212
+ if (quietMs < TRACE_LINGER_AFTER_FINISH_MS && waitedFinish < ACTIVE_SPAN_FORCE_FLUSH_MS) {
1213
+ const remainingQuiet = TRACE_LINGER_AFTER_FINISH_MS - quietMs;
1214
+ const remainingGuard = ACTIVE_SPAN_FORCE_FLUSH_MS - waitedFinish;
1215
+ scheduleIdleFlush(Math.max(10, Math.min(remainingQuiet, remainingGuard)));
1216
+ return;
1217
+ }
1218
+ }
1201
1219
  flushed = true;
1202
1220
  clearTimers();
1203
1221
  try {
@@ -1273,6 +1291,7 @@ function reproMiddleware(cfg) {
1273
1291
  };
1274
1292
  const spanKey = normalizeSpanId(evt.spanId);
1275
1293
  if (evt.type === 'enter') {
1294
+ lastEventAt = Date.now();
1276
1295
  if (spanKey) {
1277
1296
  activeSpans.add(spanKey);
1278
1297
  }
@@ -1281,6 +1300,7 @@ function reproMiddleware(cfg) {
1281
1300
  }
1282
1301
  }
1283
1302
  else if (evt.type === 'exit') {
1303
+ lastEventAt = Date.now();
1284
1304
  if (spanKey && activeSpans.has(spanKey)) {
1285
1305
  activeSpans.delete(spanKey);
1286
1306
  }
@@ -1321,6 +1341,9 @@ function reproMiddleware(cfg) {
1321
1341
  if (finished && flushed)
1322
1342
  return;
1323
1343
  finished = true;
1344
+ if (finishedAt === null) {
1345
+ finishedAt = Date.now();
1346
+ }
1324
1347
  if (capturedBody === undefined && chunks.length) {
1325
1348
  const buf = Buffer.isBuffer(chunks[0])
1326
1349
  ? Buffer.concat(chunks.map(c => (Buffer.isBuffer(c) ? c : Buffer.from(String(c)))))
@@ -1411,14 +1434,7 @@ function reproMiddleware(cfg) {
1411
1434
  if (__TRACER_READY) {
1412
1435
  bumpIdle();
1413
1436
  const hardDeadlineMs = Math.max(TRACE_FLUSH_DELAY_MS + TRACE_LINGER_AFTER_FINISH_MS, TRACE_IDLE_FLUSH_MS + TRACE_FLUSH_DELAY_MS);
1414
- hardStopTimer = setTimeout(() => {
1415
- if (hasActiveWork()) {
1416
- scheduleIdleFlush();
1417
- hardStopTimer = setTimeout(() => doFlush(true), TRACE_LINGER_AFTER_FINISH_MS);
1418
- return;
1419
- }
1420
- doFlush(true);
1421
- }, hardDeadlineMs);
1437
+ hardStopTimer = setTimeout(() => doFlush(true), hardDeadlineMs);
1422
1438
  }
1423
1439
  else {
1424
1440
  doFlush(true);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repro-nest",
3
- "version": "0.0.199",
3
+ "version": "0.0.201",
4
4
  "description": "Repro Nest SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -1372,12 +1372,15 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
1372
1372
  let unsubscribe: undefined | (() => void);
1373
1373
  let flushed = false;
1374
1374
  let finished = false;
1375
+ let finishedAt: number | null = null;
1376
+ let lastEventAt: number = Date.now();
1375
1377
  let idleTimer: NodeJS.Timeout | null = null;
1376
1378
  let hardStopTimer: NodeJS.Timeout | null = null;
1377
1379
  let drainTimer: NodeJS.Timeout | null = null;
1378
1380
  let flushPayload: null | (() => void) = null;
1379
1381
  const activeSpans = new Set<string>();
1380
1382
  let anonymousSpanDepth = 0;
1383
+ const ACTIVE_SPAN_FORCE_FLUSH_MS = 30000; // safety guard against leaks
1381
1384
 
1382
1385
  const clearTimers = () => {
1383
1386
  if (idleTimer) {
@@ -1396,21 +1399,37 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
1396
1399
 
1397
1400
  const hasActiveWork = () => activeSpans.size > 0 || anonymousSpanDepth > 0;
1398
1401
 
1399
- const scheduleIdleFlush = () => {
1402
+ const scheduleIdleFlush = (delay: number = TRACE_IDLE_FLUSH_MS) => {
1400
1403
  if (!finished || flushed) return;
1401
1404
  if (hasActiveWork()) return;
1402
1405
  if (idleTimer) {
1403
1406
  try { clearTimeout(idleTimer); } catch {}
1404
1407
  }
1405
- idleTimer = setTimeout(() => doFlush(false), TRACE_IDLE_FLUSH_MS);
1408
+ idleTimer = setTimeout(() => doFlush(false), delay);
1406
1409
  };
1407
1410
 
1408
1411
  const doFlush = (force: boolean = false) => {
1409
1412
  if (flushed) return;
1410
- if (!force && hasActiveWork()) {
1411
- scheduleIdleFlush();
1413
+ const now = Date.now();
1414
+ const stillActive = hasActiveWork();
1415
+ const quietMs = now - lastEventAt;
1416
+ const waitedFinish = finishedAt === null ? 0 : now - finishedAt;
1417
+
1418
+ // If work is still active and we haven't been quiet long enough, defer.
1419
+ if (stillActive && !force) {
1420
+ const remaining = Math.max(0, TRACE_LINGER_AFTER_FINISH_MS - quietMs);
1421
+ scheduleIdleFlush(Math.max(remaining, 10));
1412
1422
  return;
1413
1423
  }
1424
+ if (stillActive && force) {
1425
+ // Allow forced flush after either linger window of silence or max guard.
1426
+ if (quietMs < TRACE_LINGER_AFTER_FINISH_MS && waitedFinish < ACTIVE_SPAN_FORCE_FLUSH_MS) {
1427
+ const remainingQuiet = TRACE_LINGER_AFTER_FINISH_MS - quietMs;
1428
+ const remainingGuard = ACTIVE_SPAN_FORCE_FLUSH_MS - waitedFinish;
1429
+ scheduleIdleFlush(Math.max(10, Math.min(remainingQuiet, remainingGuard)));
1430
+ return;
1431
+ }
1432
+ }
1414
1433
  flushed = true;
1415
1434
  clearTimers();
1416
1435
  try { unsubscribe && unsubscribe(); } catch {}
@@ -1482,12 +1501,14 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
1482
1501
 
1483
1502
  const spanKey = normalizeSpanId(evt.spanId);
1484
1503
  if (evt.type === 'enter') {
1504
+ lastEventAt = Date.now();
1485
1505
  if (spanKey) {
1486
1506
  activeSpans.add(spanKey);
1487
1507
  } else {
1488
1508
  anonymousSpanDepth = Math.max(0, anonymousSpanDepth + 1);
1489
1509
  }
1490
1510
  } else if (evt.type === 'exit') {
1511
+ lastEventAt = Date.now();
1491
1512
  if (spanKey && activeSpans.has(spanKey)) {
1492
1513
  activeSpans.delete(spanKey);
1493
1514
  } else if (!spanKey && anonymousSpanDepth > 0) {
@@ -1531,6 +1552,9 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
1531
1552
  const handleDone = () => {
1532
1553
  if (finished && flushed) return;
1533
1554
  finished = true;
1555
+ if (finishedAt === null) {
1556
+ finishedAt = Date.now();
1557
+ }
1534
1558
  if (capturedBody === undefined && chunks.length) {
1535
1559
  const buf = Buffer.isBuffer(chunks[0])
1536
1560
  ? Buffer.concat(chunks.map(c => (Buffer.isBuffer(c) ? c : Buffer.from(String(c)))))
@@ -1623,14 +1647,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
1623
1647
  TRACE_FLUSH_DELAY_MS + TRACE_LINGER_AFTER_FINISH_MS,
1624
1648
  TRACE_IDLE_FLUSH_MS + TRACE_FLUSH_DELAY_MS,
1625
1649
  );
1626
- hardStopTimer = setTimeout(() => {
1627
- if (hasActiveWork()) {
1628
- scheduleIdleFlush();
1629
- hardStopTimer = setTimeout(() => doFlush(true), TRACE_LINGER_AFTER_FINISH_MS);
1630
- return;
1631
- }
1632
- doFlush(true);
1633
- }, hardDeadlineMs);
1650
+ hardStopTimer = setTimeout(() => doFlush(true), hardDeadlineMs);
1634
1651
  } else {
1635
1652
  doFlush(true);
1636
1653
  }