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
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.5";
|
|
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;
|
|
@@ -2892,7 +2894,7 @@ var init_http = __esm({
|
|
|
2892
2894
|
* Start a replay session by fetching historical traces.
|
|
2893
2895
|
* Blocking call - creates an experiment and returns lightweight item references.
|
|
2894
2896
|
*/
|
|
2895
|
-
async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetIds, graderIds, dbBranchSettings, attempts, includeOriginalMetadata, onlyWithAssertions, judgeAssertions, experimentalSelectiveReplay) {
|
|
2897
|
+
async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetIds, graderIds, dbBranchSettings, attempts, includeOriginalMetadata, onlyWithAssertions, judgeAssertions, experimentalSelectiveReplay, notes) {
|
|
2896
2898
|
const payload = { traceFunctionKey };
|
|
2897
2899
|
if (experimentalSelectiveReplay !== void 0) {
|
|
2898
2900
|
payload.experimentalSelectiveReplay = experimentalSelectiveReplay;
|
|
@@ -2906,6 +2908,9 @@ var init_http = __esm({
|
|
|
2906
2908
|
if (name !== void 0) {
|
|
2907
2909
|
payload.name = name;
|
|
2908
2910
|
}
|
|
2911
|
+
if (notes !== void 0) {
|
|
2912
|
+
payload.notes = notes;
|
|
2913
|
+
}
|
|
2909
2914
|
if (codeChangeDescription !== void 0) {
|
|
2910
2915
|
payload.codeChangeDescription = codeChangeDescription;
|
|
2911
2916
|
}
|
|
@@ -3117,6 +3122,28 @@ var init_http = __esm({
|
|
|
3117
3122
|
{ timeout: REPLAY_COMPLETE_REQUEST_TIMEOUT_MS }
|
|
3118
3123
|
);
|
|
3119
3124
|
}
|
|
3125
|
+
async interruptReplay(experimentId) {
|
|
3126
|
+
try {
|
|
3127
|
+
await this.request(
|
|
3128
|
+
"/api/sdk/replay/interrupt",
|
|
3129
|
+
experimentIdWireFields(experimentId),
|
|
3130
|
+
{ timeout: REPLAY_INTERRUPT_REQUEST_TIMEOUT_MS }
|
|
3131
|
+
);
|
|
3132
|
+
return true;
|
|
3133
|
+
} catch {
|
|
3134
|
+
return false;
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
async heartbeatReplay(experimentId) {
|
|
3138
|
+
try {
|
|
3139
|
+
await this.request(
|
|
3140
|
+
"/api/sdk/replay/heartbeat",
|
|
3141
|
+
experimentIdWireFields(experimentId),
|
|
3142
|
+
{ timeout: REPLAY_HEARTBEAT_REQUEST_TIMEOUT_MS }
|
|
3143
|
+
);
|
|
3144
|
+
} catch {
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3120
3147
|
async getReseedSource(traceId) {
|
|
3121
3148
|
return this.get(
|
|
3122
3149
|
`/api/sdk/traces/${encodeURIComponent(traceId)}/reseedSource`
|
|
@@ -3620,6 +3647,114 @@ var init_integrationFixture = __esm({
|
|
|
3620
3647
|
}
|
|
3621
3648
|
});
|
|
3622
3649
|
|
|
3650
|
+
// src/replayHeartbeat.ts
|
|
3651
|
+
function startReplayHeartbeat(send) {
|
|
3652
|
+
let stopped = false;
|
|
3653
|
+
let inFlight;
|
|
3654
|
+
const timer = setInterval(() => {
|
|
3655
|
+
if (stopped || inFlight !== void 0) {
|
|
3656
|
+
return;
|
|
3657
|
+
}
|
|
3658
|
+
inFlight = send().catch(() => void 0).finally(() => {
|
|
3659
|
+
inFlight = void 0;
|
|
3660
|
+
});
|
|
3661
|
+
}, REPLAY_HEARTBEAT_INTERVAL_MS);
|
|
3662
|
+
unrefTimer(timer);
|
|
3663
|
+
return {
|
|
3664
|
+
stop: async () => {
|
|
3665
|
+
stopped = true;
|
|
3666
|
+
clearInterval(timer);
|
|
3667
|
+
await inFlight;
|
|
3668
|
+
}
|
|
3669
|
+
};
|
|
3670
|
+
}
|
|
3671
|
+
var REPLAY_HEARTBEAT_INTERVAL_MS;
|
|
3672
|
+
var init_replayHeartbeat = __esm({
|
|
3673
|
+
"src/replayHeartbeat.ts"() {
|
|
3674
|
+
"use strict";
|
|
3675
|
+
init_unrefTimer();
|
|
3676
|
+
REPLAY_HEARTBEAT_INTERVAL_MS = 6e4;
|
|
3677
|
+
}
|
|
3678
|
+
});
|
|
3679
|
+
|
|
3680
|
+
// src/replayInterrupt.ts
|
|
3681
|
+
function signalCapableProcess() {
|
|
3682
|
+
if (typeof process === "undefined" || typeof process.on !== "function") {
|
|
3683
|
+
return void 0;
|
|
3684
|
+
}
|
|
3685
|
+
return process;
|
|
3686
|
+
}
|
|
3687
|
+
function describeReplayInterrupt(experimentId, markedInterrupted) {
|
|
3688
|
+
if (markedInterrupted) {
|
|
3689
|
+
return `[replay] Experiment ${experimentId} interrupted.`;
|
|
3690
|
+
}
|
|
3691
|
+
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.`;
|
|
3692
|
+
}
|
|
3693
|
+
function reportInterrupted(experimentId, markedInterrupted) {
|
|
3694
|
+
try {
|
|
3695
|
+
console.error(describeReplayInterrupt(experimentId, markedInterrupted));
|
|
3696
|
+
} catch {
|
|
3697
|
+
}
|
|
3698
|
+
}
|
|
3699
|
+
function installReplayInterruptHandlers(target) {
|
|
3700
|
+
const host = signalCapableProcess();
|
|
3701
|
+
if (host === void 0) {
|
|
3702
|
+
return { remove: () => {
|
|
3703
|
+
}, interrupted: () => false };
|
|
3704
|
+
}
|
|
3705
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
3706
|
+
const removeHandlers = () => {
|
|
3707
|
+
for (const [signal, handler] of handlers) {
|
|
3708
|
+
host.removeListener(signal, handler);
|
|
3709
|
+
}
|
|
3710
|
+
handlers.clear();
|
|
3711
|
+
};
|
|
3712
|
+
let interrupting = false;
|
|
3713
|
+
const handleSignal = async (signal) => {
|
|
3714
|
+
const hostHandlesSignal = host.listenerCount(signal) > 1;
|
|
3715
|
+
const markedInterrupted = await target.interrupt();
|
|
3716
|
+
removeHandlers();
|
|
3717
|
+
if (target.onInterrupt !== void 0) {
|
|
3718
|
+
try {
|
|
3719
|
+
target.onInterrupt({
|
|
3720
|
+
experimentId: target.experimentId,
|
|
3721
|
+
experimentUrl: target.experimentUrl,
|
|
3722
|
+
signal,
|
|
3723
|
+
markedInterrupted
|
|
3724
|
+
});
|
|
3725
|
+
} catch {
|
|
3726
|
+
}
|
|
3727
|
+
return;
|
|
3728
|
+
}
|
|
3729
|
+
reportInterrupted(target.experimentId, markedInterrupted);
|
|
3730
|
+
if (!hostHandlesSignal) {
|
|
3731
|
+
host.kill(host.pid, signal);
|
|
3732
|
+
}
|
|
3733
|
+
};
|
|
3734
|
+
for (const signal of REPLAY_INTERRUPT_SIGNALS) {
|
|
3735
|
+
const handler = () => {
|
|
3736
|
+
if (interrupting) {
|
|
3737
|
+
return;
|
|
3738
|
+
}
|
|
3739
|
+
interrupting = true;
|
|
3740
|
+
void handleSignal(signal);
|
|
3741
|
+
};
|
|
3742
|
+
handlers.set(signal, handler);
|
|
3743
|
+
host.on(signal, handler);
|
|
3744
|
+
}
|
|
3745
|
+
return { remove: removeHandlers, interrupted: () => interrupting };
|
|
3746
|
+
}
|
|
3747
|
+
var REPLAY_INTERRUPT_SIGNALS;
|
|
3748
|
+
var init_replayInterrupt = __esm({
|
|
3749
|
+
"src/replayInterrupt.ts"() {
|
|
3750
|
+
"use strict";
|
|
3751
|
+
REPLAY_INTERRUPT_SIGNALS = [
|
|
3752
|
+
"SIGINT",
|
|
3753
|
+
"SIGTERM"
|
|
3754
|
+
];
|
|
3755
|
+
}
|
|
3756
|
+
});
|
|
3757
|
+
|
|
3623
3758
|
// src/selectiveReplay.ts
|
|
3624
3759
|
function replayNodeKey(node) {
|
|
3625
3760
|
return JSON.stringify([node.traceFunctionKey, node.spanName]);
|
|
@@ -4529,7 +4664,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4529
4664
|
const includeDbBranchLease = dbBranchEnabled(options?.dbBranch) && options?.dryRun !== true;
|
|
4530
4665
|
let session;
|
|
4531
4666
|
if (options?.resume === void 0) {
|
|
4532
|
-
const
|
|
4667
|
+
const started = await httpClient.startReplay(
|
|
4533
4668
|
traceFunctionKey,
|
|
4534
4669
|
// limit is meaningless with explicit traceIds (the ID list determines
|
|
4535
4670
|
// the count), so it's omitted from the request entirely.
|
|
@@ -4550,9 +4685,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4550
4685
|
options?.adaptInputs !== void 0,
|
|
4551
4686
|
options?.onlyWithAssertions,
|
|
4552
4687
|
options?.judgeAssertions,
|
|
4553
|
-
options?.experimentalSelectiveReplay
|
|
4688
|
+
options?.experimentalSelectiveReplay,
|
|
4689
|
+
options?.notes
|
|
4554
4690
|
);
|
|
4555
|
-
if (
|
|
4691
|
+
if (started.items.length === 0) {
|
|
4556
4692
|
try {
|
|
4557
4693
|
console.warn(
|
|
4558
4694
|
`Bitfab: no traces matched "${traceFunctionKey}", so this replay ran nothing. Capture a trace, or seed one with seedTrace, before replaying.`
|
|
@@ -4561,10 +4697,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4561
4697
|
}
|
|
4562
4698
|
}
|
|
4563
4699
|
session = {
|
|
4564
|
-
experimentId:
|
|
4565
|
-
experimentUrl:
|
|
4700
|
+
experimentId: started.experimentId,
|
|
4701
|
+
experimentUrl: started.experimentUrl,
|
|
4566
4702
|
attempts: requestedAttempts,
|
|
4567
|
-
work: expandReplayWork(
|
|
4703
|
+
work: expandReplayWork(started.items, requestedAttempts),
|
|
4568
4704
|
finished: [],
|
|
4569
4705
|
includeDbBranchLease,
|
|
4570
4706
|
dbBranchSettings: resolveDbBranchSettings(options?.dbBranch)
|
|
@@ -4579,9 +4715,68 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4579
4715
|
options.dbBranch
|
|
4580
4716
|
);
|
|
4581
4717
|
}
|
|
4582
|
-
const { experimentId
|
|
4718
|
+
const { experimentId } = session;
|
|
4719
|
+
const fullExperimentUrl = `${serviceUrl}${session.experimentUrl}`;
|
|
4720
|
+
const heartbeat = startReplayHeartbeat(
|
|
4721
|
+
() => httpClient.heartbeatReplay(experimentId)
|
|
4722
|
+
);
|
|
4723
|
+
const interruptReplay = async () => {
|
|
4724
|
+
await heartbeat.stop();
|
|
4725
|
+
return httpClient.interruptReplay(experimentId);
|
|
4726
|
+
};
|
|
4727
|
+
const interruptHandlers = installReplayInterruptHandlers({
|
|
4728
|
+
experimentId,
|
|
4729
|
+
experimentUrl: fullExperimentUrl,
|
|
4730
|
+
interrupt: interruptReplay,
|
|
4731
|
+
onInterrupt: options?.onInterrupt
|
|
4732
|
+
});
|
|
4733
|
+
let completeReturned = false;
|
|
4734
|
+
const markCompleteReturned = () => {
|
|
4735
|
+
completeReturned = true;
|
|
4736
|
+
void heartbeat.stop();
|
|
4737
|
+
};
|
|
4738
|
+
try {
|
|
4739
|
+
return await runReplaySession(
|
|
4740
|
+
httpClient,
|
|
4741
|
+
fn,
|
|
4742
|
+
session,
|
|
4743
|
+
fullExperimentUrl,
|
|
4744
|
+
maxConcurrency,
|
|
4745
|
+
interruptHandlers.remove,
|
|
4746
|
+
markCompleteReturned,
|
|
4747
|
+
interruptHandlers.interrupted,
|
|
4748
|
+
options,
|
|
4749
|
+
registeredOverrides
|
|
4750
|
+
);
|
|
4751
|
+
} catch (error) {
|
|
4752
|
+
if (!completeReturned && !interruptHandlers.interrupted()) {
|
|
4753
|
+
await interruptReplay();
|
|
4754
|
+
}
|
|
4755
|
+
throw error;
|
|
4756
|
+
} finally {
|
|
4757
|
+
interruptHandlers.remove();
|
|
4758
|
+
await heartbeat.stop();
|
|
4759
|
+
}
|
|
4760
|
+
}
|
|
4761
|
+
function throwIfInterrupted(interrupted, items, experimentId, experimentUrl) {
|
|
4762
|
+
if (!interrupted()) {
|
|
4763
|
+
return;
|
|
4764
|
+
}
|
|
4765
|
+
const cause = new BitfabError(
|
|
4766
|
+
`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}.`,
|
|
4767
|
+
experimentUrl
|
|
4768
|
+
);
|
|
4769
|
+
throw new ReplayError(
|
|
4770
|
+
cause.message,
|
|
4771
|
+
items,
|
|
4772
|
+
experimentId,
|
|
4773
|
+
experimentUrl,
|
|
4774
|
+
cause
|
|
4775
|
+
);
|
|
4776
|
+
}
|
|
4777
|
+
async function runReplaySession(httpClient, fn, session, fullExperimentUrl, maxConcurrency, stopInterruptingOnSignal, markCompleteReturned, interrupted, options, registeredOverrides) {
|
|
4778
|
+
const { experimentId, attempts } = session;
|
|
4583
4779
|
const mockStrategy = options?.mock ?? "marked";
|
|
4584
|
-
const fullExperimentUrl = `${serviceUrl}${experimentUrl}`;
|
|
4585
4780
|
try {
|
|
4586
4781
|
options?.onExperimentStart?.({
|
|
4587
4782
|
experimentId,
|
|
@@ -4731,7 +4926,15 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4731
4926
|
);
|
|
4732
4927
|
const resultItems = [...carriedOverItems, ...ranItems];
|
|
4733
4928
|
if (options?.dryRun === true) {
|
|
4929
|
+
throwIfInterrupted(
|
|
4930
|
+
interrupted,
|
|
4931
|
+
resultItems,
|
|
4932
|
+
experimentId,
|
|
4933
|
+
fullExperimentUrl
|
|
4934
|
+
);
|
|
4935
|
+
stopInterruptingOnSignal();
|
|
4734
4936
|
await httpClient.completeReplay(experimentId).catch(() => void 0);
|
|
4937
|
+
markCompleteReturned();
|
|
4735
4938
|
const dryResult = {
|
|
4736
4939
|
items: resultItems,
|
|
4737
4940
|
experimentId,
|
|
@@ -4756,12 +4959,15 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4756
4959
|
ranItems[index].traceId = readBack;
|
|
4757
4960
|
}
|
|
4758
4961
|
}
|
|
4962
|
+
throwIfInterrupted(interrupted, resultItems, experimentId, fullExperimentUrl);
|
|
4963
|
+
stopInterruptingOnSignal();
|
|
4759
4964
|
const completeResult = await preserveReplayFailure(
|
|
4760
4965
|
() => httpClient.completeReplay(experimentId),
|
|
4761
4966
|
resultItems,
|
|
4762
4967
|
experimentId,
|
|
4763
4968
|
fullExperimentUrl
|
|
4764
4969
|
);
|
|
4970
|
+
markCompleteReturned();
|
|
4765
4971
|
const serverTraceIds = completeResult.traceIds;
|
|
4766
4972
|
const replayTokens = completeResult.tokens;
|
|
4767
4973
|
if (serverTraceIds !== void 0) {
|
|
@@ -4865,6 +5071,8 @@ var init_replay = __esm({
|
|
|
4865
5071
|
init_mockOverride();
|
|
4866
5072
|
init_randomUuid();
|
|
4867
5073
|
init_replayContext();
|
|
5074
|
+
init_replayHeartbeat();
|
|
5075
|
+
init_replayInterrupt();
|
|
4868
5076
|
init_selectiveReplayRuntime();
|
|
4869
5077
|
init_serialize();
|
|
4870
5078
|
REPLAY_PERSISTENCE_TIMEOUT_MS = 3e4;
|
|
@@ -11641,6 +11849,7 @@ init_replay();
|
|
|
11641
11849
|
// src/replayRegistry.ts
|
|
11642
11850
|
init_errors();
|
|
11643
11851
|
init_replay();
|
|
11852
|
+
init_replayInterrupt();
|
|
11644
11853
|
function defineReplayRegistry(registry) {
|
|
11645
11854
|
return registry;
|
|
11646
11855
|
}
|