bitfab 0.61.3 → 0.61.4
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-54EGXUZ3.js} +27 -2
- package/dist/chunk-54EGXUZ3.js.map +1 -0
- package/dist/{chunk-IZUZ5NBB.js → chunk-PSAEQ4OW.js} +176 -10
- package/dist/chunk-PSAEQ4OW.js.map +1 -0
- package/dist/{chunk-HVEZHLVQ.js → chunk-VWRCLZCA.js} +26 -2
- package/dist/{chunk-HVEZHLVQ.js.map → chunk-VWRCLZCA.js.map} +1 -1
- package/dist/{chunk-WOHIAKZF.js → chunk-WPYBJGTG.js} +21 -3
- package/dist/chunk-WPYBJGTG.js.map +1 -0
- package/dist/{chunk-PAUXHTNF.js → chunk-XGRQMQ2R.js} +6 -6
- package/dist/{chunk-PAUXHTNF.js.map → chunk-XGRQMQ2R.js.map} +1 -1
- package/dist/{http-BYICBOWP.js → http-C2TCBBQV.js} +2 -2
- package/dist/{http-3IYU5Z54.js → http-ONLJPP6W.js} +2 -2
- package/dist/index.cjs +214 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.js +3 -3
- package/dist/node.cjs +214 -9
- 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-RM24QKEA.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-C2TCBBQV.js.map} +0 -0
- /package/dist/{http-BYICBOWP.js.map → http-ONLJPP6W.js.map} +0 -0
- /package/dist/{replay-SXHX5BBU.js.map → replay-RM24QKEA.js.map} +0 -0
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
flushTraces,
|
|
8
8
|
parseRetryAfterMs,
|
|
9
9
|
serializePayloadBody
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-VWRCLZCA.js";
|
|
11
11
|
export {
|
|
12
12
|
BitfabError,
|
|
13
13
|
HttpClient,
|
|
@@ -18,4 +18,4 @@ export {
|
|
|
18
18
|
parseRetryAfterMs,
|
|
19
19
|
serializePayloadBody
|
|
20
20
|
};
|
|
21
|
-
//# sourceMappingURL=http-
|
|
21
|
+
//# sourceMappingURL=http-C2TCBBQV.js.map
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
flushTraces,
|
|
8
8
|
parseRetryAfterMs,
|
|
9
9
|
serializePayloadBody
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-54EGXUZ3.js";
|
|
11
11
|
import "./chunk-BAOIRE5T.js";
|
|
12
12
|
export {
|
|
13
13
|
BitfabError,
|
|
@@ -19,4 +19,4 @@ export {
|
|
|
19
19
|
parseRetryAfterMs,
|
|
20
20
|
serializePayloadBody
|
|
21
21
|
};
|
|
22
|
-
//# sourceMappingURL=http-
|
|
22
|
+
//# sourceMappingURL=http-ONLJPP6W.js.map
|
package/dist/index.cjs
CHANGED
|
@@ -51,7 +51,7 @@ var __version__, __packageName__;
|
|
|
51
51
|
var init_version_generated = __esm({
|
|
52
52
|
"src/version.generated.ts"() {
|
|
53
53
|
"use strict";
|
|
54
|
-
__version__ = "0.61.
|
|
54
|
+
__version__ = "0.61.4";
|
|
55
55
|
__packageName__ = "bitfab";
|
|
56
56
|
}
|
|
57
57
|
});
|
|
@@ -2286,7 +2286,7 @@ function warnDroppedExternalTrace(error) {
|
|
|
2286
2286
|
function experimentIdWireFields(experimentId) {
|
|
2287
2287
|
return { experimentId, testRunId: experimentId };
|
|
2288
2288
|
}
|
|
2289
|
-
var REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS, REPLAY_COMPLETE_REQUEST_TIMEOUT_MS, OTLP_TRACES_ENDPOINT, RETRYABLE_STATUSES, EXIT_FLUSH_TIMEOUT_MS, DEFAULT_LIFECYCLE_TIMEOUT_MS2, pendingTracePromises, liveHttpClients, carrierSeq, HttpClient;
|
|
2289
|
+
var REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS, REPLAY_COMPLETE_REQUEST_TIMEOUT_MS, REPLAY_INTERRUPT_REQUEST_TIMEOUT_MS, REPLAY_HEARTBEAT_REQUEST_TIMEOUT_MS, OTLP_TRACES_ENDPOINT, RETRYABLE_STATUSES, EXIT_FLUSH_TIMEOUT_MS, DEFAULT_LIFECYCLE_TIMEOUT_MS2, pendingTracePromises, liveHttpClients, carrierSeq, HttpClient;
|
|
2290
2290
|
var init_http = __esm({
|
|
2291
2291
|
"src/http.ts"() {
|
|
2292
2292
|
"use strict";
|
|
@@ -2305,6 +2305,8 @@ var init_http = __esm({
|
|
|
2305
2305
|
init_warnOnce();
|
|
2306
2306
|
REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS = 3e5;
|
|
2307
2307
|
REPLAY_COMPLETE_REQUEST_TIMEOUT_MS = 12e4;
|
|
2308
|
+
REPLAY_INTERRUPT_REQUEST_TIMEOUT_MS = 5e3;
|
|
2309
|
+
REPLAY_HEARTBEAT_REQUEST_TIMEOUT_MS = 5e3;
|
|
2308
2310
|
OTLP_TRACES_ENDPOINT = "/api/sdk/otel/v1/traces";
|
|
2309
2311
|
RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
2310
2312
|
EXIT_FLUSH_TIMEOUT_MS = 5e3;
|
|
@@ -3117,6 +3119,28 @@ var init_http = __esm({
|
|
|
3117
3119
|
{ timeout: REPLAY_COMPLETE_REQUEST_TIMEOUT_MS }
|
|
3118
3120
|
);
|
|
3119
3121
|
}
|
|
3122
|
+
async interruptReplay(experimentId) {
|
|
3123
|
+
try {
|
|
3124
|
+
await this.request(
|
|
3125
|
+
"/api/sdk/replay/interrupt",
|
|
3126
|
+
experimentIdWireFields(experimentId),
|
|
3127
|
+
{ timeout: REPLAY_INTERRUPT_REQUEST_TIMEOUT_MS }
|
|
3128
|
+
);
|
|
3129
|
+
return true;
|
|
3130
|
+
} catch {
|
|
3131
|
+
return false;
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
async heartbeatReplay(experimentId) {
|
|
3135
|
+
try {
|
|
3136
|
+
await this.request(
|
|
3137
|
+
"/api/sdk/replay/heartbeat",
|
|
3138
|
+
experimentIdWireFields(experimentId),
|
|
3139
|
+
{ timeout: REPLAY_HEARTBEAT_REQUEST_TIMEOUT_MS }
|
|
3140
|
+
);
|
|
3141
|
+
} catch {
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3120
3144
|
async getReseedSource(traceId) {
|
|
3121
3145
|
return this.get(
|
|
3122
3146
|
`/api/sdk/traces/${encodeURIComponent(traceId)}/reseedSource`
|
|
@@ -3620,6 +3644,114 @@ var init_integrationFixture = __esm({
|
|
|
3620
3644
|
}
|
|
3621
3645
|
});
|
|
3622
3646
|
|
|
3647
|
+
// src/replayHeartbeat.ts
|
|
3648
|
+
function startReplayHeartbeat(send) {
|
|
3649
|
+
let stopped = false;
|
|
3650
|
+
let inFlight;
|
|
3651
|
+
const timer = setInterval(() => {
|
|
3652
|
+
if (stopped || inFlight !== void 0) {
|
|
3653
|
+
return;
|
|
3654
|
+
}
|
|
3655
|
+
inFlight = send().catch(() => void 0).finally(() => {
|
|
3656
|
+
inFlight = void 0;
|
|
3657
|
+
});
|
|
3658
|
+
}, REPLAY_HEARTBEAT_INTERVAL_MS);
|
|
3659
|
+
unrefTimer(timer);
|
|
3660
|
+
return {
|
|
3661
|
+
stop: async () => {
|
|
3662
|
+
stopped = true;
|
|
3663
|
+
clearInterval(timer);
|
|
3664
|
+
await inFlight;
|
|
3665
|
+
}
|
|
3666
|
+
};
|
|
3667
|
+
}
|
|
3668
|
+
var REPLAY_HEARTBEAT_INTERVAL_MS;
|
|
3669
|
+
var init_replayHeartbeat = __esm({
|
|
3670
|
+
"src/replayHeartbeat.ts"() {
|
|
3671
|
+
"use strict";
|
|
3672
|
+
init_unrefTimer();
|
|
3673
|
+
REPLAY_HEARTBEAT_INTERVAL_MS = 6e4;
|
|
3674
|
+
}
|
|
3675
|
+
});
|
|
3676
|
+
|
|
3677
|
+
// src/replayInterrupt.ts
|
|
3678
|
+
function signalCapableProcess() {
|
|
3679
|
+
if (typeof process === "undefined" || typeof process.on !== "function") {
|
|
3680
|
+
return void 0;
|
|
3681
|
+
}
|
|
3682
|
+
return process;
|
|
3683
|
+
}
|
|
3684
|
+
function describeReplayInterrupt(experimentId, markedInterrupted) {
|
|
3685
|
+
if (markedInterrupted) {
|
|
3686
|
+
return `[replay] Experiment ${experimentId} interrupted.`;
|
|
3687
|
+
}
|
|
3688
|
+
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.`;
|
|
3689
|
+
}
|
|
3690
|
+
function reportInterrupted(experimentId, markedInterrupted) {
|
|
3691
|
+
try {
|
|
3692
|
+
console.error(describeReplayInterrupt(experimentId, markedInterrupted));
|
|
3693
|
+
} catch {
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
function installReplayInterruptHandlers(target) {
|
|
3697
|
+
const host = signalCapableProcess();
|
|
3698
|
+
if (host === void 0) {
|
|
3699
|
+
return { remove: () => {
|
|
3700
|
+
}, interrupted: () => false };
|
|
3701
|
+
}
|
|
3702
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
3703
|
+
const removeHandlers = () => {
|
|
3704
|
+
for (const [signal, handler] of handlers) {
|
|
3705
|
+
host.removeListener(signal, handler);
|
|
3706
|
+
}
|
|
3707
|
+
handlers.clear();
|
|
3708
|
+
};
|
|
3709
|
+
let interrupting = false;
|
|
3710
|
+
const handleSignal = async (signal) => {
|
|
3711
|
+
const hostHandlesSignal = host.listenerCount(signal) > 1;
|
|
3712
|
+
const markedInterrupted = await target.interrupt();
|
|
3713
|
+
removeHandlers();
|
|
3714
|
+
if (target.onInterrupt !== void 0) {
|
|
3715
|
+
try {
|
|
3716
|
+
target.onInterrupt({
|
|
3717
|
+
experimentId: target.experimentId,
|
|
3718
|
+
experimentUrl: target.experimentUrl,
|
|
3719
|
+
signal,
|
|
3720
|
+
markedInterrupted
|
|
3721
|
+
});
|
|
3722
|
+
} catch {
|
|
3723
|
+
}
|
|
3724
|
+
return;
|
|
3725
|
+
}
|
|
3726
|
+
reportInterrupted(target.experimentId, markedInterrupted);
|
|
3727
|
+
if (!hostHandlesSignal) {
|
|
3728
|
+
host.kill(host.pid, signal);
|
|
3729
|
+
}
|
|
3730
|
+
};
|
|
3731
|
+
for (const signal of REPLAY_INTERRUPT_SIGNALS) {
|
|
3732
|
+
const handler = () => {
|
|
3733
|
+
if (interrupting) {
|
|
3734
|
+
return;
|
|
3735
|
+
}
|
|
3736
|
+
interrupting = true;
|
|
3737
|
+
void handleSignal(signal);
|
|
3738
|
+
};
|
|
3739
|
+
handlers.set(signal, handler);
|
|
3740
|
+
host.on(signal, handler);
|
|
3741
|
+
}
|
|
3742
|
+
return { remove: removeHandlers, interrupted: () => interrupting };
|
|
3743
|
+
}
|
|
3744
|
+
var REPLAY_INTERRUPT_SIGNALS;
|
|
3745
|
+
var init_replayInterrupt = __esm({
|
|
3746
|
+
"src/replayInterrupt.ts"() {
|
|
3747
|
+
"use strict";
|
|
3748
|
+
REPLAY_INTERRUPT_SIGNALS = [
|
|
3749
|
+
"SIGINT",
|
|
3750
|
+
"SIGTERM"
|
|
3751
|
+
];
|
|
3752
|
+
}
|
|
3753
|
+
});
|
|
3754
|
+
|
|
3623
3755
|
// src/selectiveReplay.ts
|
|
3624
3756
|
function replayNodeKey(node) {
|
|
3625
3757
|
return JSON.stringify([node.traceFunctionKey, node.spanName]);
|
|
@@ -4529,7 +4661,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4529
4661
|
const includeDbBranchLease = dbBranchEnabled(options?.dbBranch) && options?.dryRun !== true;
|
|
4530
4662
|
let session;
|
|
4531
4663
|
if (options?.resume === void 0) {
|
|
4532
|
-
const
|
|
4664
|
+
const started = await httpClient.startReplay(
|
|
4533
4665
|
traceFunctionKey,
|
|
4534
4666
|
// limit is meaningless with explicit traceIds (the ID list determines
|
|
4535
4667
|
// the count), so it's omitted from the request entirely.
|
|
@@ -4552,7 +4684,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4552
4684
|
options?.judgeAssertions,
|
|
4553
4685
|
options?.experimentalSelectiveReplay
|
|
4554
4686
|
);
|
|
4555
|
-
if (
|
|
4687
|
+
if (started.items.length === 0) {
|
|
4556
4688
|
try {
|
|
4557
4689
|
console.warn(
|
|
4558
4690
|
`Bitfab: no traces matched "${traceFunctionKey}", so this replay ran nothing. Capture a trace, or seed one with seedTrace, before replaying.`
|
|
@@ -4561,10 +4693,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4561
4693
|
}
|
|
4562
4694
|
}
|
|
4563
4695
|
session = {
|
|
4564
|
-
experimentId:
|
|
4565
|
-
experimentUrl:
|
|
4696
|
+
experimentId: started.experimentId,
|
|
4697
|
+
experimentUrl: started.experimentUrl,
|
|
4566
4698
|
attempts: requestedAttempts,
|
|
4567
|
-
work: expandReplayWork(
|
|
4699
|
+
work: expandReplayWork(started.items, requestedAttempts),
|
|
4568
4700
|
finished: [],
|
|
4569
4701
|
includeDbBranchLease,
|
|
4570
4702
|
dbBranchSettings: resolveDbBranchSettings(options?.dbBranch)
|
|
@@ -4579,9 +4711,68 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4579
4711
|
options.dbBranch
|
|
4580
4712
|
);
|
|
4581
4713
|
}
|
|
4582
|
-
const { experimentId
|
|
4714
|
+
const { experimentId } = session;
|
|
4715
|
+
const fullExperimentUrl = `${serviceUrl}${session.experimentUrl}`;
|
|
4716
|
+
const heartbeat = startReplayHeartbeat(
|
|
4717
|
+
() => httpClient.heartbeatReplay(experimentId)
|
|
4718
|
+
);
|
|
4719
|
+
const interruptReplay = async () => {
|
|
4720
|
+
await heartbeat.stop();
|
|
4721
|
+
return httpClient.interruptReplay(experimentId);
|
|
4722
|
+
};
|
|
4723
|
+
const interruptHandlers = installReplayInterruptHandlers({
|
|
4724
|
+
experimentId,
|
|
4725
|
+
experimentUrl: fullExperimentUrl,
|
|
4726
|
+
interrupt: interruptReplay,
|
|
4727
|
+
onInterrupt: options?.onInterrupt
|
|
4728
|
+
});
|
|
4729
|
+
let completeReturned = false;
|
|
4730
|
+
const markCompleteReturned = () => {
|
|
4731
|
+
completeReturned = true;
|
|
4732
|
+
void heartbeat.stop();
|
|
4733
|
+
};
|
|
4734
|
+
try {
|
|
4735
|
+
return await runReplaySession(
|
|
4736
|
+
httpClient,
|
|
4737
|
+
fn,
|
|
4738
|
+
session,
|
|
4739
|
+
fullExperimentUrl,
|
|
4740
|
+
maxConcurrency,
|
|
4741
|
+
interruptHandlers.remove,
|
|
4742
|
+
markCompleteReturned,
|
|
4743
|
+
interruptHandlers.interrupted,
|
|
4744
|
+
options,
|
|
4745
|
+
registeredOverrides
|
|
4746
|
+
);
|
|
4747
|
+
} catch (error) {
|
|
4748
|
+
if (!completeReturned && !interruptHandlers.interrupted()) {
|
|
4749
|
+
await interruptReplay();
|
|
4750
|
+
}
|
|
4751
|
+
throw error;
|
|
4752
|
+
} finally {
|
|
4753
|
+
interruptHandlers.remove();
|
|
4754
|
+
await heartbeat.stop();
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
function throwIfInterrupted(interrupted, items, experimentId, experimentUrl) {
|
|
4758
|
+
if (!interrupted()) {
|
|
4759
|
+
return;
|
|
4760
|
+
}
|
|
4761
|
+
const cause = new BitfabError(
|
|
4762
|
+
`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}.`,
|
|
4763
|
+
experimentUrl
|
|
4764
|
+
);
|
|
4765
|
+
throw new ReplayError(
|
|
4766
|
+
cause.message,
|
|
4767
|
+
items,
|
|
4768
|
+
experimentId,
|
|
4769
|
+
experimentUrl,
|
|
4770
|
+
cause
|
|
4771
|
+
);
|
|
4772
|
+
}
|
|
4773
|
+
async function runReplaySession(httpClient, fn, session, fullExperimentUrl, maxConcurrency, stopInterruptingOnSignal, markCompleteReturned, interrupted, options, registeredOverrides) {
|
|
4774
|
+
const { experimentId, attempts } = session;
|
|
4583
4775
|
const mockStrategy = options?.mock ?? "marked";
|
|
4584
|
-
const fullExperimentUrl = `${serviceUrl}${experimentUrl}`;
|
|
4585
4776
|
try {
|
|
4586
4777
|
options?.onExperimentStart?.({
|
|
4587
4778
|
experimentId,
|
|
@@ -4731,7 +4922,15 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4731
4922
|
);
|
|
4732
4923
|
const resultItems = [...carriedOverItems, ...ranItems];
|
|
4733
4924
|
if (options?.dryRun === true) {
|
|
4925
|
+
throwIfInterrupted(
|
|
4926
|
+
interrupted,
|
|
4927
|
+
resultItems,
|
|
4928
|
+
experimentId,
|
|
4929
|
+
fullExperimentUrl
|
|
4930
|
+
);
|
|
4931
|
+
stopInterruptingOnSignal();
|
|
4734
4932
|
await httpClient.completeReplay(experimentId).catch(() => void 0);
|
|
4933
|
+
markCompleteReturned();
|
|
4735
4934
|
const dryResult = {
|
|
4736
4935
|
items: resultItems,
|
|
4737
4936
|
experimentId,
|
|
@@ -4756,12 +4955,15 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4756
4955
|
ranItems[index].traceId = readBack;
|
|
4757
4956
|
}
|
|
4758
4957
|
}
|
|
4958
|
+
throwIfInterrupted(interrupted, resultItems, experimentId, fullExperimentUrl);
|
|
4959
|
+
stopInterruptingOnSignal();
|
|
4759
4960
|
const completeResult = await preserveReplayFailure(
|
|
4760
4961
|
() => httpClient.completeReplay(experimentId),
|
|
4761
4962
|
resultItems,
|
|
4762
4963
|
experimentId,
|
|
4763
4964
|
fullExperimentUrl
|
|
4764
4965
|
);
|
|
4966
|
+
markCompleteReturned();
|
|
4765
4967
|
const serverTraceIds = completeResult.traceIds;
|
|
4766
4968
|
const replayTokens = completeResult.tokens;
|
|
4767
4969
|
if (serverTraceIds !== void 0) {
|
|
@@ -4865,6 +5067,8 @@ var init_replay = __esm({
|
|
|
4865
5067
|
init_mockOverride();
|
|
4866
5068
|
init_randomUuid();
|
|
4867
5069
|
init_replayContext();
|
|
5070
|
+
init_replayHeartbeat();
|
|
5071
|
+
init_replayInterrupt();
|
|
4868
5072
|
init_selectiveReplayRuntime();
|
|
4869
5073
|
init_serialize();
|
|
4870
5074
|
REPLAY_PERSISTENCE_TIMEOUT_MS = 3e4;
|
|
@@ -11641,6 +11845,7 @@ init_replay();
|
|
|
11641
11845
|
// src/replayRegistry.ts
|
|
11642
11846
|
init_errors();
|
|
11643
11847
|
init_replay();
|
|
11848
|
+
init_replayInterrupt();
|
|
11644
11849
|
function defineReplayRegistry(registry) {
|
|
11645
11850
|
return registry;
|
|
11646
11851
|
}
|