@bitfab/sdk 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-7BU5LUMR.js → chunk-2ZUNCC2Q.js} +30 -3
- package/dist/{chunk-7BU5LUMR.js.map → chunk-2ZUNCC2Q.js.map} +1 -1
- package/dist/{chunk-34UFYAE6.js → chunk-44KG6B5D.js} +178 -11
- package/dist/chunk-44KG6B5D.js.map +1 -0
- package/dist/{chunk-TZSTKVZF.js → chunk-NISRFSN2.js} +6 -6
- package/dist/{chunk-TZSTKVZF.js.map → chunk-NISRFSN2.js.map} +1 -1
- package/dist/{chunk-OWXP3YXA.js → chunk-Y4IGLP2Z.js} +28 -4
- package/dist/chunk-Y4IGLP2Z.js.map +1 -0
- package/dist/{chunk-65QKCHI2.js → chunk-ZPVWVXWC.js} +31 -3
- package/dist/chunk-ZPVWVXWC.js.map +1 -0
- package/dist/{http-ZWENQXQA.js → http-JPHWWKL5.js} +2 -2
- package/dist/{http-VTIALK4P.js → http-VKYDN6PO.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-YQG3OGDN.js → replay-FP3L3X7Q.js} +3 -3
- package/dist/replayCli.js +2 -2
- package/dist/seedCli.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-34UFYAE6.js.map +0 -1
- package/dist/chunk-65QKCHI2.js.map +0 -1
- package/dist/chunk-OWXP3YXA.js.map +0 -1
- /package/dist/{http-VTIALK4P.js.map → http-JPHWWKL5.js.map} +0 -0
- /package/dist/{http-ZWENQXQA.js.map → http-VKYDN6PO.js.map} +0 -0
- /package/dist/{replay-YQG3OGDN.js.map → replay-FP3L3X7Q.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -42,7 +42,7 @@ var __version__, __packageName__;
|
|
|
42
42
|
var init_version_generated = __esm({
|
|
43
43
|
"src/version.generated.ts"() {
|
|
44
44
|
"use strict";
|
|
45
|
-
__version__ = "0.61.
|
|
45
|
+
__version__ = "0.61.5";
|
|
46
46
|
__packageName__ = "@bitfab/sdk";
|
|
47
47
|
}
|
|
48
48
|
});
|
|
@@ -2277,7 +2277,7 @@ function warnDroppedExternalTrace(error) {
|
|
|
2277
2277
|
function experimentIdWireFields(experimentId) {
|
|
2278
2278
|
return { experimentId, testRunId: experimentId };
|
|
2279
2279
|
}
|
|
2280
|
-
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;
|
|
2280
|
+
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;
|
|
2281
2281
|
var init_http = __esm({
|
|
2282
2282
|
"src/http.ts"() {
|
|
2283
2283
|
"use strict";
|
|
@@ -2296,6 +2296,8 @@ var init_http = __esm({
|
|
|
2296
2296
|
init_warnOnce();
|
|
2297
2297
|
REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS = 3e5;
|
|
2298
2298
|
REPLAY_COMPLETE_REQUEST_TIMEOUT_MS = 12e4;
|
|
2299
|
+
REPLAY_INTERRUPT_REQUEST_TIMEOUT_MS = 5e3;
|
|
2300
|
+
REPLAY_HEARTBEAT_REQUEST_TIMEOUT_MS = 5e3;
|
|
2299
2301
|
OTLP_TRACES_ENDPOINT = "/api/sdk/otel/v1/traces";
|
|
2300
2302
|
RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
2301
2303
|
EXIT_FLUSH_TIMEOUT_MS = 5e3;
|
|
@@ -2883,7 +2885,7 @@ var init_http = __esm({
|
|
|
2883
2885
|
* Start a replay session by fetching historical traces.
|
|
2884
2886
|
* Blocking call - creates an experiment and returns lightweight item references.
|
|
2885
2887
|
*/
|
|
2886
|
-
async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetIds, graderIds, dbBranchSettings, attempts, includeOriginalMetadata, onlyWithAssertions, judgeAssertions, experimentalSelectiveReplay) {
|
|
2888
|
+
async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetIds, graderIds, dbBranchSettings, attempts, includeOriginalMetadata, onlyWithAssertions, judgeAssertions, experimentalSelectiveReplay, notes) {
|
|
2887
2889
|
const payload = { traceFunctionKey };
|
|
2888
2890
|
if (experimentalSelectiveReplay !== void 0) {
|
|
2889
2891
|
payload.experimentalSelectiveReplay = experimentalSelectiveReplay;
|
|
@@ -2897,6 +2899,9 @@ var init_http = __esm({
|
|
|
2897
2899
|
if (name !== void 0) {
|
|
2898
2900
|
payload.name = name;
|
|
2899
2901
|
}
|
|
2902
|
+
if (notes !== void 0) {
|
|
2903
|
+
payload.notes = notes;
|
|
2904
|
+
}
|
|
2900
2905
|
if (codeChangeDescription !== void 0) {
|
|
2901
2906
|
payload.codeChangeDescription = codeChangeDescription;
|
|
2902
2907
|
}
|
|
@@ -3108,6 +3113,28 @@ var init_http = __esm({
|
|
|
3108
3113
|
{ timeout: REPLAY_COMPLETE_REQUEST_TIMEOUT_MS }
|
|
3109
3114
|
);
|
|
3110
3115
|
}
|
|
3116
|
+
async interruptReplay(experimentId) {
|
|
3117
|
+
try {
|
|
3118
|
+
await this.request(
|
|
3119
|
+
"/api/sdk/replay/interrupt",
|
|
3120
|
+
experimentIdWireFields(experimentId),
|
|
3121
|
+
{ timeout: REPLAY_INTERRUPT_REQUEST_TIMEOUT_MS }
|
|
3122
|
+
);
|
|
3123
|
+
return true;
|
|
3124
|
+
} catch {
|
|
3125
|
+
return false;
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
async heartbeatReplay(experimentId) {
|
|
3129
|
+
try {
|
|
3130
|
+
await this.request(
|
|
3131
|
+
"/api/sdk/replay/heartbeat",
|
|
3132
|
+
experimentIdWireFields(experimentId),
|
|
3133
|
+
{ timeout: REPLAY_HEARTBEAT_REQUEST_TIMEOUT_MS }
|
|
3134
|
+
);
|
|
3135
|
+
} catch {
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3111
3138
|
async getReseedSource(traceId) {
|
|
3112
3139
|
return this.get(
|
|
3113
3140
|
`/api/sdk/traces/${encodeURIComponent(traceId)}/reseedSource`
|
|
@@ -3611,6 +3638,114 @@ var init_integrationFixture = __esm({
|
|
|
3611
3638
|
}
|
|
3612
3639
|
});
|
|
3613
3640
|
|
|
3641
|
+
// src/replayHeartbeat.ts
|
|
3642
|
+
function startReplayHeartbeat(send) {
|
|
3643
|
+
let stopped = false;
|
|
3644
|
+
let inFlight;
|
|
3645
|
+
const timer = setInterval(() => {
|
|
3646
|
+
if (stopped || inFlight !== void 0) {
|
|
3647
|
+
return;
|
|
3648
|
+
}
|
|
3649
|
+
inFlight = send().catch(() => void 0).finally(() => {
|
|
3650
|
+
inFlight = void 0;
|
|
3651
|
+
});
|
|
3652
|
+
}, REPLAY_HEARTBEAT_INTERVAL_MS);
|
|
3653
|
+
unrefTimer(timer);
|
|
3654
|
+
return {
|
|
3655
|
+
stop: async () => {
|
|
3656
|
+
stopped = true;
|
|
3657
|
+
clearInterval(timer);
|
|
3658
|
+
await inFlight;
|
|
3659
|
+
}
|
|
3660
|
+
};
|
|
3661
|
+
}
|
|
3662
|
+
var REPLAY_HEARTBEAT_INTERVAL_MS;
|
|
3663
|
+
var init_replayHeartbeat = __esm({
|
|
3664
|
+
"src/replayHeartbeat.ts"() {
|
|
3665
|
+
"use strict";
|
|
3666
|
+
init_unrefTimer();
|
|
3667
|
+
REPLAY_HEARTBEAT_INTERVAL_MS = 6e4;
|
|
3668
|
+
}
|
|
3669
|
+
});
|
|
3670
|
+
|
|
3671
|
+
// src/replayInterrupt.ts
|
|
3672
|
+
function signalCapableProcess() {
|
|
3673
|
+
if (typeof process === "undefined" || typeof process.on !== "function") {
|
|
3674
|
+
return void 0;
|
|
3675
|
+
}
|
|
3676
|
+
return process;
|
|
3677
|
+
}
|
|
3678
|
+
function describeReplayInterrupt(experimentId, markedInterrupted) {
|
|
3679
|
+
if (markedInterrupted) {
|
|
3680
|
+
return `[replay] Experiment ${experimentId} interrupted.`;
|
|
3681
|
+
}
|
|
3682
|
+
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.`;
|
|
3683
|
+
}
|
|
3684
|
+
function reportInterrupted(experimentId, markedInterrupted) {
|
|
3685
|
+
try {
|
|
3686
|
+
console.error(describeReplayInterrupt(experimentId, markedInterrupted));
|
|
3687
|
+
} catch {
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
function installReplayInterruptHandlers(target) {
|
|
3691
|
+
const host = signalCapableProcess();
|
|
3692
|
+
if (host === void 0) {
|
|
3693
|
+
return { remove: () => {
|
|
3694
|
+
}, interrupted: () => false };
|
|
3695
|
+
}
|
|
3696
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
3697
|
+
const removeHandlers = () => {
|
|
3698
|
+
for (const [signal, handler] of handlers) {
|
|
3699
|
+
host.removeListener(signal, handler);
|
|
3700
|
+
}
|
|
3701
|
+
handlers.clear();
|
|
3702
|
+
};
|
|
3703
|
+
let interrupting = false;
|
|
3704
|
+
const handleSignal = async (signal) => {
|
|
3705
|
+
const hostHandlesSignal = host.listenerCount(signal) > 1;
|
|
3706
|
+
const markedInterrupted = await target.interrupt();
|
|
3707
|
+
removeHandlers();
|
|
3708
|
+
if (target.onInterrupt !== void 0) {
|
|
3709
|
+
try {
|
|
3710
|
+
target.onInterrupt({
|
|
3711
|
+
experimentId: target.experimentId,
|
|
3712
|
+
experimentUrl: target.experimentUrl,
|
|
3713
|
+
signal,
|
|
3714
|
+
markedInterrupted
|
|
3715
|
+
});
|
|
3716
|
+
} catch {
|
|
3717
|
+
}
|
|
3718
|
+
return;
|
|
3719
|
+
}
|
|
3720
|
+
reportInterrupted(target.experimentId, markedInterrupted);
|
|
3721
|
+
if (!hostHandlesSignal) {
|
|
3722
|
+
host.kill(host.pid, signal);
|
|
3723
|
+
}
|
|
3724
|
+
};
|
|
3725
|
+
for (const signal of REPLAY_INTERRUPT_SIGNALS) {
|
|
3726
|
+
const handler = () => {
|
|
3727
|
+
if (interrupting) {
|
|
3728
|
+
return;
|
|
3729
|
+
}
|
|
3730
|
+
interrupting = true;
|
|
3731
|
+
void handleSignal(signal);
|
|
3732
|
+
};
|
|
3733
|
+
handlers.set(signal, handler);
|
|
3734
|
+
host.on(signal, handler);
|
|
3735
|
+
}
|
|
3736
|
+
return { remove: removeHandlers, interrupted: () => interrupting };
|
|
3737
|
+
}
|
|
3738
|
+
var REPLAY_INTERRUPT_SIGNALS;
|
|
3739
|
+
var init_replayInterrupt = __esm({
|
|
3740
|
+
"src/replayInterrupt.ts"() {
|
|
3741
|
+
"use strict";
|
|
3742
|
+
REPLAY_INTERRUPT_SIGNALS = [
|
|
3743
|
+
"SIGINT",
|
|
3744
|
+
"SIGTERM"
|
|
3745
|
+
];
|
|
3746
|
+
}
|
|
3747
|
+
});
|
|
3748
|
+
|
|
3614
3749
|
// src/selectiveReplay.ts
|
|
3615
3750
|
function replayNodeKey(node) {
|
|
3616
3751
|
return JSON.stringify([node.traceFunctionKey, node.spanName]);
|
|
@@ -4520,7 +4655,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4520
4655
|
const includeDbBranchLease = dbBranchEnabled(options?.dbBranch) && options?.dryRun !== true;
|
|
4521
4656
|
let session;
|
|
4522
4657
|
if (options?.resume === void 0) {
|
|
4523
|
-
const
|
|
4658
|
+
const started = await httpClient.startReplay(
|
|
4524
4659
|
traceFunctionKey,
|
|
4525
4660
|
// limit is meaningless with explicit traceIds (the ID list determines
|
|
4526
4661
|
// the count), so it's omitted from the request entirely.
|
|
@@ -4541,9 +4676,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4541
4676
|
options?.adaptInputs !== void 0,
|
|
4542
4677
|
options?.onlyWithAssertions,
|
|
4543
4678
|
options?.judgeAssertions,
|
|
4544
|
-
options?.experimentalSelectiveReplay
|
|
4679
|
+
options?.experimentalSelectiveReplay,
|
|
4680
|
+
options?.notes
|
|
4545
4681
|
);
|
|
4546
|
-
if (
|
|
4682
|
+
if (started.items.length === 0) {
|
|
4547
4683
|
try {
|
|
4548
4684
|
console.warn(
|
|
4549
4685
|
`Bitfab: no traces matched "${traceFunctionKey}", so this replay ran nothing. Capture a trace, or seed one with seedTrace, before replaying.`
|
|
@@ -4552,10 +4688,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4552
4688
|
}
|
|
4553
4689
|
}
|
|
4554
4690
|
session = {
|
|
4555
|
-
experimentId:
|
|
4556
|
-
experimentUrl:
|
|
4691
|
+
experimentId: started.experimentId,
|
|
4692
|
+
experimentUrl: started.experimentUrl,
|
|
4557
4693
|
attempts: requestedAttempts,
|
|
4558
|
-
work: expandReplayWork(
|
|
4694
|
+
work: expandReplayWork(started.items, requestedAttempts),
|
|
4559
4695
|
finished: [],
|
|
4560
4696
|
includeDbBranchLease,
|
|
4561
4697
|
dbBranchSettings: resolveDbBranchSettings(options?.dbBranch)
|
|
@@ -4570,9 +4706,68 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4570
4706
|
options.dbBranch
|
|
4571
4707
|
);
|
|
4572
4708
|
}
|
|
4573
|
-
const { experimentId
|
|
4709
|
+
const { experimentId } = session;
|
|
4710
|
+
const fullExperimentUrl = `${serviceUrl}${session.experimentUrl}`;
|
|
4711
|
+
const heartbeat = startReplayHeartbeat(
|
|
4712
|
+
() => httpClient.heartbeatReplay(experimentId)
|
|
4713
|
+
);
|
|
4714
|
+
const interruptReplay = async () => {
|
|
4715
|
+
await heartbeat.stop();
|
|
4716
|
+
return httpClient.interruptReplay(experimentId);
|
|
4717
|
+
};
|
|
4718
|
+
const interruptHandlers = installReplayInterruptHandlers({
|
|
4719
|
+
experimentId,
|
|
4720
|
+
experimentUrl: fullExperimentUrl,
|
|
4721
|
+
interrupt: interruptReplay,
|
|
4722
|
+
onInterrupt: options?.onInterrupt
|
|
4723
|
+
});
|
|
4724
|
+
let completeReturned = false;
|
|
4725
|
+
const markCompleteReturned = () => {
|
|
4726
|
+
completeReturned = true;
|
|
4727
|
+
void heartbeat.stop();
|
|
4728
|
+
};
|
|
4729
|
+
try {
|
|
4730
|
+
return await runReplaySession(
|
|
4731
|
+
httpClient,
|
|
4732
|
+
fn,
|
|
4733
|
+
session,
|
|
4734
|
+
fullExperimentUrl,
|
|
4735
|
+
maxConcurrency,
|
|
4736
|
+
interruptHandlers.remove,
|
|
4737
|
+
markCompleteReturned,
|
|
4738
|
+
interruptHandlers.interrupted,
|
|
4739
|
+
options,
|
|
4740
|
+
registeredOverrides
|
|
4741
|
+
);
|
|
4742
|
+
} catch (error) {
|
|
4743
|
+
if (!completeReturned && !interruptHandlers.interrupted()) {
|
|
4744
|
+
await interruptReplay();
|
|
4745
|
+
}
|
|
4746
|
+
throw error;
|
|
4747
|
+
} finally {
|
|
4748
|
+
interruptHandlers.remove();
|
|
4749
|
+
await heartbeat.stop();
|
|
4750
|
+
}
|
|
4751
|
+
}
|
|
4752
|
+
function throwIfInterrupted(interrupted, items, experimentId, experimentUrl) {
|
|
4753
|
+
if (!interrupted()) {
|
|
4754
|
+
return;
|
|
4755
|
+
}
|
|
4756
|
+
const cause = new BitfabError(
|
|
4757
|
+
`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}.`,
|
|
4758
|
+
experimentUrl
|
|
4759
|
+
);
|
|
4760
|
+
throw new ReplayError(
|
|
4761
|
+
cause.message,
|
|
4762
|
+
items,
|
|
4763
|
+
experimentId,
|
|
4764
|
+
experimentUrl,
|
|
4765
|
+
cause
|
|
4766
|
+
);
|
|
4767
|
+
}
|
|
4768
|
+
async function runReplaySession(httpClient, fn, session, fullExperimentUrl, maxConcurrency, stopInterruptingOnSignal, markCompleteReturned, interrupted, options, registeredOverrides) {
|
|
4769
|
+
const { experimentId, attempts } = session;
|
|
4574
4770
|
const mockStrategy = options?.mock ?? "marked";
|
|
4575
|
-
const fullExperimentUrl = `${serviceUrl}${experimentUrl}`;
|
|
4576
4771
|
try {
|
|
4577
4772
|
options?.onExperimentStart?.({
|
|
4578
4773
|
experimentId,
|
|
@@ -4722,7 +4917,15 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4722
4917
|
);
|
|
4723
4918
|
const resultItems = [...carriedOverItems, ...ranItems];
|
|
4724
4919
|
if (options?.dryRun === true) {
|
|
4920
|
+
throwIfInterrupted(
|
|
4921
|
+
interrupted,
|
|
4922
|
+
resultItems,
|
|
4923
|
+
experimentId,
|
|
4924
|
+
fullExperimentUrl
|
|
4925
|
+
);
|
|
4926
|
+
stopInterruptingOnSignal();
|
|
4725
4927
|
await httpClient.completeReplay(experimentId).catch(() => void 0);
|
|
4928
|
+
markCompleteReturned();
|
|
4726
4929
|
const dryResult = {
|
|
4727
4930
|
items: resultItems,
|
|
4728
4931
|
experimentId,
|
|
@@ -4747,12 +4950,15 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4747
4950
|
ranItems[index].traceId = readBack;
|
|
4748
4951
|
}
|
|
4749
4952
|
}
|
|
4953
|
+
throwIfInterrupted(interrupted, resultItems, experimentId, fullExperimentUrl);
|
|
4954
|
+
stopInterruptingOnSignal();
|
|
4750
4955
|
const completeResult = await preserveReplayFailure(
|
|
4751
4956
|
() => httpClient.completeReplay(experimentId),
|
|
4752
4957
|
resultItems,
|
|
4753
4958
|
experimentId,
|
|
4754
4959
|
fullExperimentUrl
|
|
4755
4960
|
);
|
|
4961
|
+
markCompleteReturned();
|
|
4756
4962
|
const serverTraceIds = completeResult.traceIds;
|
|
4757
4963
|
const replayTokens = completeResult.tokens;
|
|
4758
4964
|
if (serverTraceIds !== void 0) {
|
|
@@ -4856,6 +5062,8 @@ var init_replay = __esm({
|
|
|
4856
5062
|
init_mockOverride();
|
|
4857
5063
|
init_randomUuid();
|
|
4858
5064
|
init_replayContext();
|
|
5065
|
+
init_replayHeartbeat();
|
|
5066
|
+
init_replayInterrupt();
|
|
4859
5067
|
init_selectiveReplayRuntime();
|
|
4860
5068
|
init_serialize();
|
|
4861
5069
|
REPLAY_PERSISTENCE_TIMEOUT_MS = 3e4;
|
|
@@ -11632,6 +11840,7 @@ init_replay();
|
|
|
11632
11840
|
// src/replayRegistry.ts
|
|
11633
11841
|
init_errors();
|
|
11634
11842
|
init_replay();
|
|
11843
|
+
init_replayInterrupt();
|
|
11635
11844
|
function defineReplayRegistry(registry) {
|
|
11636
11845
|
return registry;
|
|
11637
11846
|
}
|