bitfab 0.61.3 → 0.61.5
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/dist/{chunk-6IMUONDN.js → chunk-4TRH3X3S.js} +31 -3
- package/dist/chunk-4TRH3X3S.js.map +1 -0
- package/dist/{chunk-IZUZ5NBB.js → chunk-7OXXBUGQ.js} +178 -11
- package/dist/chunk-7OXXBUGQ.js.map +1 -0
- package/dist/{chunk-PAUXHTNF.js → chunk-KOANTEGQ.js} +6 -6
- package/dist/{chunk-PAUXHTNF.js.map → chunk-KOANTEGQ.js.map} +1 -1
- package/dist/{chunk-HVEZHLVQ.js → chunk-R5RMFRHX.js} +30 -3
- package/dist/{chunk-HVEZHLVQ.js.map → chunk-R5RMFRHX.js.map} +1 -1
- package/dist/{chunk-WOHIAKZF.js → chunk-VD5PS6WE.js} +28 -4
- package/dist/chunk-VD5PS6WE.js.map +1 -0
- package/dist/{http-BYICBOWP.js → http-CR2SEMK4.js} +2 -2
- package/dist/{http-3IYU5Z54.js → http-SK2QESXJ.js} +2 -2
- package/dist/index.cjs +220 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -4
- package/dist/index.d.ts +24 -4
- package/dist/index.js +3 -3
- package/dist/node.cjs +220 -11
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +3 -3
- package/dist/{replay-SXHX5BBU.js → replay-YSR7FD4I.js} +3 -3
- package/dist/replayCli.js +2 -2
- package/dist/seedCli.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-6IMUONDN.js.map +0 -1
- package/dist/chunk-IZUZ5NBB.js.map +0 -1
- package/dist/chunk-WOHIAKZF.js.map +0 -1
- /package/dist/{http-3IYU5Z54.js.map → http-CR2SEMK4.js.map} +0 -0
- /package/dist/{http-BYICBOWP.js.map → http-SK2QESXJ.js.map} +0 -0
- /package/dist/{replay-SXHX5BBU.js.map → replay-YSR7FD4I.js.map} +0 -0
|
@@ -4,8 +4,9 @@ import {
|
|
|
4
4
|
flushTraces,
|
|
5
5
|
gitRunner,
|
|
6
6
|
replayContextReady,
|
|
7
|
-
runWithReplayContext
|
|
8
|
-
|
|
7
|
+
runWithReplayContext,
|
|
8
|
+
unrefTimer
|
|
9
|
+
} from "./chunk-4TRH3X3S.js";
|
|
9
10
|
import {
|
|
10
11
|
runWithCaptureSuppressed,
|
|
11
12
|
warnOnce
|
|
@@ -437,6 +438,101 @@ function fallbackUuidV4() {
|
|
|
437
438
|
});
|
|
438
439
|
}
|
|
439
440
|
|
|
441
|
+
// src/replayHeartbeat.ts
|
|
442
|
+
var REPLAY_HEARTBEAT_INTERVAL_MS = 6e4;
|
|
443
|
+
function startReplayHeartbeat(send) {
|
|
444
|
+
let stopped = false;
|
|
445
|
+
let inFlight;
|
|
446
|
+
const timer = setInterval(() => {
|
|
447
|
+
if (stopped || inFlight !== void 0) {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
inFlight = send().catch(() => void 0).finally(() => {
|
|
451
|
+
inFlight = void 0;
|
|
452
|
+
});
|
|
453
|
+
}, REPLAY_HEARTBEAT_INTERVAL_MS);
|
|
454
|
+
unrefTimer(timer);
|
|
455
|
+
return {
|
|
456
|
+
stop: async () => {
|
|
457
|
+
stopped = true;
|
|
458
|
+
clearInterval(timer);
|
|
459
|
+
await inFlight;
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// src/replayInterrupt.ts
|
|
465
|
+
var REPLAY_INTERRUPT_SIGNALS = [
|
|
466
|
+
"SIGINT",
|
|
467
|
+
"SIGTERM"
|
|
468
|
+
];
|
|
469
|
+
function signalCapableProcess() {
|
|
470
|
+
if (typeof process === "undefined" || typeof process.on !== "function") {
|
|
471
|
+
return void 0;
|
|
472
|
+
}
|
|
473
|
+
return process;
|
|
474
|
+
}
|
|
475
|
+
function describeReplayInterrupt(experimentId, markedInterrupted) {
|
|
476
|
+
if (markedInterrupted) {
|
|
477
|
+
return `[replay] Experiment ${experimentId} interrupted.`;
|
|
478
|
+
}
|
|
479
|
+
return `[replay] Experiment ${experimentId} stopped, but Bitfab could not mark it interrupted. It will be marked interrupted within 15 minutes. A resume in the next two minutes may be refused as still running.`;
|
|
480
|
+
}
|
|
481
|
+
function reportInterrupted(experimentId, markedInterrupted) {
|
|
482
|
+
try {
|
|
483
|
+
console.error(describeReplayInterrupt(experimentId, markedInterrupted));
|
|
484
|
+
} catch {
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
function installReplayInterruptHandlers(target) {
|
|
488
|
+
const host = signalCapableProcess();
|
|
489
|
+
if (host === void 0) {
|
|
490
|
+
return { remove: () => {
|
|
491
|
+
}, interrupted: () => false };
|
|
492
|
+
}
|
|
493
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
494
|
+
const removeHandlers = () => {
|
|
495
|
+
for (const [signal, handler] of handlers) {
|
|
496
|
+
host.removeListener(signal, handler);
|
|
497
|
+
}
|
|
498
|
+
handlers.clear();
|
|
499
|
+
};
|
|
500
|
+
let interrupting = false;
|
|
501
|
+
const handleSignal = async (signal) => {
|
|
502
|
+
const hostHandlesSignal = host.listenerCount(signal) > 1;
|
|
503
|
+
const markedInterrupted = await target.interrupt();
|
|
504
|
+
removeHandlers();
|
|
505
|
+
if (target.onInterrupt !== void 0) {
|
|
506
|
+
try {
|
|
507
|
+
target.onInterrupt({
|
|
508
|
+
experimentId: target.experimentId,
|
|
509
|
+
experimentUrl: target.experimentUrl,
|
|
510
|
+
signal,
|
|
511
|
+
markedInterrupted
|
|
512
|
+
});
|
|
513
|
+
} catch {
|
|
514
|
+
}
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
reportInterrupted(target.experimentId, markedInterrupted);
|
|
518
|
+
if (!hostHandlesSignal) {
|
|
519
|
+
host.kill(host.pid, signal);
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
for (const signal of REPLAY_INTERRUPT_SIGNALS) {
|
|
523
|
+
const handler = () => {
|
|
524
|
+
if (interrupting) {
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
interrupting = true;
|
|
528
|
+
void handleSignal(signal);
|
|
529
|
+
};
|
|
530
|
+
handlers.set(signal, handler);
|
|
531
|
+
host.on(signal, handler);
|
|
532
|
+
}
|
|
533
|
+
return { remove: removeHandlers, interrupted: () => interrupting };
|
|
534
|
+
}
|
|
535
|
+
|
|
440
536
|
// src/selectiveReplay.ts
|
|
441
537
|
function replayNodeKey(node) {
|
|
442
538
|
return JSON.stringify([node.traceFunctionKey, node.spanName]);
|
|
@@ -1357,7 +1453,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
1357
1453
|
const includeDbBranchLease = dbBranchEnabled(options?.dbBranch) && options?.dryRun !== true;
|
|
1358
1454
|
let session;
|
|
1359
1455
|
if (options?.resume === void 0) {
|
|
1360
|
-
const
|
|
1456
|
+
const started = await httpClient.startReplay(
|
|
1361
1457
|
traceFunctionKey,
|
|
1362
1458
|
// limit is meaningless with explicit traceIds (the ID list determines
|
|
1363
1459
|
// the count), so it's omitted from the request entirely.
|
|
@@ -1378,9 +1474,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
1378
1474
|
options?.adaptInputs !== void 0,
|
|
1379
1475
|
options?.onlyWithAssertions,
|
|
1380
1476
|
options?.judgeAssertions,
|
|
1381
|
-
options?.experimentalSelectiveReplay
|
|
1477
|
+
options?.experimentalSelectiveReplay,
|
|
1478
|
+
options?.notes
|
|
1382
1479
|
);
|
|
1383
|
-
if (
|
|
1480
|
+
if (started.items.length === 0) {
|
|
1384
1481
|
try {
|
|
1385
1482
|
console.warn(
|
|
1386
1483
|
`Bitfab: no traces matched "${traceFunctionKey}", so this replay ran nothing. Capture a trace, or seed one with seedTrace, before replaying.`
|
|
@@ -1389,10 +1486,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
1389
1486
|
}
|
|
1390
1487
|
}
|
|
1391
1488
|
session = {
|
|
1392
|
-
experimentId:
|
|
1393
|
-
experimentUrl:
|
|
1489
|
+
experimentId: started.experimentId,
|
|
1490
|
+
experimentUrl: started.experimentUrl,
|
|
1394
1491
|
attempts: requestedAttempts,
|
|
1395
|
-
work: expandReplayWork(
|
|
1492
|
+
work: expandReplayWork(started.items, requestedAttempts),
|
|
1396
1493
|
finished: [],
|
|
1397
1494
|
includeDbBranchLease,
|
|
1398
1495
|
dbBranchSettings: resolveDbBranchSettings(options?.dbBranch)
|
|
@@ -1407,9 +1504,68 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
1407
1504
|
options.dbBranch
|
|
1408
1505
|
);
|
|
1409
1506
|
}
|
|
1410
|
-
const { experimentId
|
|
1507
|
+
const { experimentId } = session;
|
|
1508
|
+
const fullExperimentUrl = `${serviceUrl}${session.experimentUrl}`;
|
|
1509
|
+
const heartbeat = startReplayHeartbeat(
|
|
1510
|
+
() => httpClient.heartbeatReplay(experimentId)
|
|
1511
|
+
);
|
|
1512
|
+
const interruptReplay = async () => {
|
|
1513
|
+
await heartbeat.stop();
|
|
1514
|
+
return httpClient.interruptReplay(experimentId);
|
|
1515
|
+
};
|
|
1516
|
+
const interruptHandlers = installReplayInterruptHandlers({
|
|
1517
|
+
experimentId,
|
|
1518
|
+
experimentUrl: fullExperimentUrl,
|
|
1519
|
+
interrupt: interruptReplay,
|
|
1520
|
+
onInterrupt: options?.onInterrupt
|
|
1521
|
+
});
|
|
1522
|
+
let completeReturned = false;
|
|
1523
|
+
const markCompleteReturned = () => {
|
|
1524
|
+
completeReturned = true;
|
|
1525
|
+
void heartbeat.stop();
|
|
1526
|
+
};
|
|
1527
|
+
try {
|
|
1528
|
+
return await runReplaySession(
|
|
1529
|
+
httpClient,
|
|
1530
|
+
fn,
|
|
1531
|
+
session,
|
|
1532
|
+
fullExperimentUrl,
|
|
1533
|
+
maxConcurrency,
|
|
1534
|
+
interruptHandlers.remove,
|
|
1535
|
+
markCompleteReturned,
|
|
1536
|
+
interruptHandlers.interrupted,
|
|
1537
|
+
options,
|
|
1538
|
+
registeredOverrides
|
|
1539
|
+
);
|
|
1540
|
+
} catch (error) {
|
|
1541
|
+
if (!completeReturned && !interruptHandlers.interrupted()) {
|
|
1542
|
+
await interruptReplay();
|
|
1543
|
+
}
|
|
1544
|
+
throw error;
|
|
1545
|
+
} finally {
|
|
1546
|
+
interruptHandlers.remove();
|
|
1547
|
+
await heartbeat.stop();
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
function throwIfInterrupted(interrupted, items, experimentId, experimentUrl) {
|
|
1551
|
+
if (!interrupted()) {
|
|
1552
|
+
return;
|
|
1553
|
+
}
|
|
1554
|
+
const cause = new BitfabError(
|
|
1555
|
+
`Experiment ${experimentId} was interrupted, so it was not completed or graded. Resume it by passing { resume: "${experimentId}" } to replay, or with bitfab-replay --resume ${experimentId}.`,
|
|
1556
|
+
experimentUrl
|
|
1557
|
+
);
|
|
1558
|
+
throw new ReplayError(
|
|
1559
|
+
cause.message,
|
|
1560
|
+
items,
|
|
1561
|
+
experimentId,
|
|
1562
|
+
experimentUrl,
|
|
1563
|
+
cause
|
|
1564
|
+
);
|
|
1565
|
+
}
|
|
1566
|
+
async function runReplaySession(httpClient, fn, session, fullExperimentUrl, maxConcurrency, stopInterruptingOnSignal, markCompleteReturned, interrupted, options, registeredOverrides) {
|
|
1567
|
+
const { experimentId, attempts } = session;
|
|
1411
1568
|
const mockStrategy = options?.mock ?? "marked";
|
|
1412
|
-
const fullExperimentUrl = `${serviceUrl}${experimentUrl}`;
|
|
1413
1569
|
try {
|
|
1414
1570
|
options?.onExperimentStart?.({
|
|
1415
1571
|
experimentId,
|
|
@@ -1559,7 +1715,15 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
1559
1715
|
);
|
|
1560
1716
|
const resultItems = [...carriedOverItems, ...ranItems];
|
|
1561
1717
|
if (options?.dryRun === true) {
|
|
1718
|
+
throwIfInterrupted(
|
|
1719
|
+
interrupted,
|
|
1720
|
+
resultItems,
|
|
1721
|
+
experimentId,
|
|
1722
|
+
fullExperimentUrl
|
|
1723
|
+
);
|
|
1724
|
+
stopInterruptingOnSignal();
|
|
1562
1725
|
await httpClient.completeReplay(experimentId).catch(() => void 0);
|
|
1726
|
+
markCompleteReturned();
|
|
1563
1727
|
const dryResult = {
|
|
1564
1728
|
items: resultItems,
|
|
1565
1729
|
experimentId,
|
|
@@ -1584,12 +1748,15 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
1584
1748
|
ranItems[index].traceId = readBack;
|
|
1585
1749
|
}
|
|
1586
1750
|
}
|
|
1751
|
+
throwIfInterrupted(interrupted, resultItems, experimentId, fullExperimentUrl);
|
|
1752
|
+
stopInterruptingOnSignal();
|
|
1587
1753
|
const completeResult = await preserveReplayFailure(
|
|
1588
1754
|
() => httpClient.completeReplay(experimentId),
|
|
1589
1755
|
resultItems,
|
|
1590
1756
|
experimentId,
|
|
1591
1757
|
fullExperimentUrl
|
|
1592
1758
|
);
|
|
1759
|
+
markCompleteReturned();
|
|
1593
1760
|
const serverTraceIds = completeResult.traceIds;
|
|
1594
1761
|
const replayTokens = completeResult.tokens;
|
|
1595
1762
|
if (serverTraceIds !== void 0) {
|
|
@@ -1702,4 +1869,4 @@ export {
|
|
|
1702
1869
|
sleepForReplayPersistence,
|
|
1703
1870
|
replay
|
|
1704
1871
|
};
|
|
1705
|
-
//# sourceMappingURL=chunk-
|
|
1872
|
+
//# sourceMappingURL=chunk-7OXXBUGQ.js.map
|