deepline 0.1.201 → 0.1.203
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/bundling-sources/apps/play-runner-workers/src/entry.ts +30 -9
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +25 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +139 -124
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +17 -2
- package/dist/bundling-sources/shared_libs/play-runtime/bounded-dispatch.ts +52 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +154 -23
- package/dist/bundling-sources/shared_libs/play-runtime/coordinator-headers.ts +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +16 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +121 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/adaptive-admission.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-budget-state-backend.ts +22 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +86 -19
- package/dist/bundling-sources/shared_libs/play-runtime/governor/block-reserving-budget-state-backend.ts +141 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/budget-state-backend.ts +43 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +129 -41
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +40 -2
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-completion-sink.ts +92 -33
- package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +13 -5
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +122 -120
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +10 -8
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +140 -32
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api-paths.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +225 -29
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-scheduler-topology.ts +26 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +21 -0
- package/dist/bundling-sources/shared_libs/plays/dataset.ts +32 -10
- package/dist/cli/index.js +335 -77
- package/dist/cli/index.mjs +341 -83
- package/dist/index.js +3 -2
- package/dist/index.mjs +3 -2
- package/package.json +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/adaptive-tool-dispatcher.ts +0 -355
package/dist/index.js
CHANGED
|
@@ -422,10 +422,10 @@ var SDK_RELEASE = {
|
|
|
422
422
|
// 0.1.154 removes the short-lived generated enrich StepOptions recompute
|
|
423
423
|
// fields shipped in 0.1.153.
|
|
424
424
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
425
|
-
version: "0.1.
|
|
425
|
+
version: "0.1.203",
|
|
426
426
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
427
427
|
supportPolicy: {
|
|
428
|
-
latest: "0.1.
|
|
428
|
+
latest: "0.1.203",
|
|
429
429
|
minimumSupported: "0.1.53",
|
|
430
430
|
deprecatedBelow: "0.1.53",
|
|
431
431
|
commandMinimumSupported: [
|
|
@@ -4383,6 +4383,7 @@ function isDeeplineExtractorTarget(value) {
|
|
|
4383
4383
|
var PLAY_DATASET_BRAND = /* @__PURE__ */ Symbol.for("deepline.play.dataset");
|
|
4384
4384
|
var NODE_INSPECT_CUSTOM = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
|
|
4385
4385
|
var DEFAULT_MATERIALIZE_LIMIT = 1e4;
|
|
4386
|
+
var PLAY_DATASET_EXECUTION_PAGE_BYTES = 64 * 1024 * 1024;
|
|
4386
4387
|
function resolveMaterializeLimitCap() {
|
|
4387
4388
|
const raw = process.env.DEEPLINE_PLAY_DATASET_MATERIALIZE_LIMIT;
|
|
4388
4389
|
const parsed = raw ? Number(raw) : NaN;
|
package/dist/index.mjs
CHANGED
|
@@ -352,10 +352,10 @@ var SDK_RELEASE = {
|
|
|
352
352
|
// 0.1.154 removes the short-lived generated enrich StepOptions recompute
|
|
353
353
|
// fields shipped in 0.1.153.
|
|
354
354
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
355
|
-
version: "0.1.
|
|
355
|
+
version: "0.1.203",
|
|
356
356
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
357
357
|
supportPolicy: {
|
|
358
|
-
latest: "0.1.
|
|
358
|
+
latest: "0.1.203",
|
|
359
359
|
minimumSupported: "0.1.53",
|
|
360
360
|
deprecatedBelow: "0.1.53",
|
|
361
361
|
commandMinimumSupported: [
|
|
@@ -4313,6 +4313,7 @@ function isDeeplineExtractorTarget(value) {
|
|
|
4313
4313
|
var PLAY_DATASET_BRAND = /* @__PURE__ */ Symbol.for("deepline.play.dataset");
|
|
4314
4314
|
var NODE_INSPECT_CUSTOM = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
|
|
4315
4315
|
var DEFAULT_MATERIALIZE_LIMIT = 1e4;
|
|
4316
|
+
var PLAY_DATASET_EXECUTION_PAGE_BYTES = 64 * 1024 * 1024;
|
|
4316
4317
|
function resolveMaterializeLimitCap() {
|
|
4317
4318
|
const raw = process.env.DEEPLINE_PLAY_DATASET_MATERIALIZE_LIMIT;
|
|
4318
4319
|
const parsed = raw ? Number(raw) : NaN;
|
package/package.json
CHANGED
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
export type AdaptiveToolDispatchErrorKind = 'rate_limit' | 'other';
|
|
2
|
-
|
|
3
|
-
export type AdaptiveToolDispatchError = {
|
|
4
|
-
kind: AdaptiveToolDispatchErrorKind;
|
|
5
|
-
retryAfterMs?: number | null;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
export type AdaptiveToolDispatcherOptions = {
|
|
9
|
-
initialRequestsPerSecond: number;
|
|
10
|
-
maxRequestsPerSecond: number;
|
|
11
|
-
maxConcurrency: number;
|
|
12
|
-
minConcurrency?: number;
|
|
13
|
-
additiveIncreasePerWindow?: number;
|
|
14
|
-
cleanWindowMs?: number;
|
|
15
|
-
defaultLatencyMs?: number;
|
|
16
|
-
safetyFactor?: number;
|
|
17
|
-
now?: () => number;
|
|
18
|
-
sleep?: (ms: number) => Promise<void>;
|
|
19
|
-
classifyError?: (error: unknown) => AdaptiveToolDispatchError | null;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export type AdaptiveToolDispatchResult<TJob, TResult> = {
|
|
23
|
-
job: TJob;
|
|
24
|
-
index: number;
|
|
25
|
-
result: TResult | null;
|
|
26
|
-
latencyMs: number | null;
|
|
27
|
-
error?: unknown;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export type AdaptiveToolDispatcherSnapshot = {
|
|
31
|
-
currentRequestsPerSecond: number;
|
|
32
|
-
targetConcurrency: number;
|
|
33
|
-
inFlight: number;
|
|
34
|
-
queued: number;
|
|
35
|
-
maxObservedInFlight: number;
|
|
36
|
-
successCount: number;
|
|
37
|
-
errorCount: number;
|
|
38
|
-
rateLimitCount: number;
|
|
39
|
-
latencyP95Ms: number | null;
|
|
40
|
-
cooldownUntilMs: number | null;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
type QueuedJob<TJob> = {
|
|
44
|
-
job: TJob;
|
|
45
|
-
index: number;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
function positiveInteger(value: number, fallback: number): number {
|
|
49
|
-
return Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function clamp(value: number, min: number, max: number): number {
|
|
53
|
-
return Math.max(min, Math.min(max, value));
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function percentile(
|
|
57
|
-
values: readonly number[],
|
|
58
|
-
percentileValue: number,
|
|
59
|
-
): number {
|
|
60
|
-
if (values.length === 0) return 0;
|
|
61
|
-
const sorted = [...values].sort((a, b) => a - b);
|
|
62
|
-
const index = clamp(
|
|
63
|
-
Math.ceil(sorted.length * percentileValue) - 1,
|
|
64
|
-
0,
|
|
65
|
-
sorted.length - 1,
|
|
66
|
-
);
|
|
67
|
-
return sorted[index] ?? 0;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export class AdaptiveToolDispatcher<TJob, TResult> {
|
|
71
|
-
private readonly maxRequestsPerSecond: number;
|
|
72
|
-
private readonly maxConcurrency: number;
|
|
73
|
-
private readonly minConcurrency: number;
|
|
74
|
-
private readonly additiveIncreasePerWindow: number;
|
|
75
|
-
private readonly cleanWindowMs: number;
|
|
76
|
-
private readonly defaultLatencyMs: number;
|
|
77
|
-
private readonly safetyFactor: number;
|
|
78
|
-
private readonly now: () => number;
|
|
79
|
-
private readonly sleep: (ms: number) => Promise<void>;
|
|
80
|
-
private readonly classifyError: (
|
|
81
|
-
error: unknown,
|
|
82
|
-
) => AdaptiveToolDispatchError | null;
|
|
83
|
-
private readonly latencySamples: number[] = [];
|
|
84
|
-
private readonly launchTimestamps: number[] = [];
|
|
85
|
-
private readonly progressWaiters: Array<() => void> = [];
|
|
86
|
-
private currentRequestsPerSecond: number;
|
|
87
|
-
private lastIncreaseAtMs: number;
|
|
88
|
-
private successCountAtLastIncrease = 0;
|
|
89
|
-
private successCount = 0;
|
|
90
|
-
private errorCount = 0;
|
|
91
|
-
private rateLimitCount = 0;
|
|
92
|
-
private inFlight = 0;
|
|
93
|
-
private queued = 0;
|
|
94
|
-
private maxObservedInFlight = 0;
|
|
95
|
-
private cooldownUntilMs: number | null = null;
|
|
96
|
-
|
|
97
|
-
constructor(options: AdaptiveToolDispatcherOptions) {
|
|
98
|
-
this.maxRequestsPerSecond = positiveInteger(
|
|
99
|
-
options.maxRequestsPerSecond,
|
|
100
|
-
1,
|
|
101
|
-
);
|
|
102
|
-
this.minConcurrency = positiveInteger(options.minConcurrency ?? 1, 1);
|
|
103
|
-
this.maxConcurrency = Math.max(
|
|
104
|
-
this.minConcurrency,
|
|
105
|
-
positiveInteger(options.maxConcurrency, this.minConcurrency),
|
|
106
|
-
);
|
|
107
|
-
this.currentRequestsPerSecond = clamp(
|
|
108
|
-
positiveInteger(options.initialRequestsPerSecond, 1),
|
|
109
|
-
1,
|
|
110
|
-
this.maxRequestsPerSecond,
|
|
111
|
-
);
|
|
112
|
-
this.additiveIncreasePerWindow = positiveInteger(
|
|
113
|
-
options.additiveIncreasePerWindow ?? 5,
|
|
114
|
-
5,
|
|
115
|
-
);
|
|
116
|
-
this.cleanWindowMs = positiveInteger(options.cleanWindowMs ?? 1_000, 1_000);
|
|
117
|
-
this.defaultLatencyMs = positiveInteger(
|
|
118
|
-
options.defaultLatencyMs ?? 1_000,
|
|
119
|
-
1_000,
|
|
120
|
-
);
|
|
121
|
-
this.safetyFactor =
|
|
122
|
-
Number.isFinite(options.safetyFactor) && options.safetyFactor! > 0
|
|
123
|
-
? options.safetyFactor!
|
|
124
|
-
: 1.5;
|
|
125
|
-
this.now = options.now ?? Date.now;
|
|
126
|
-
this.sleep =
|
|
127
|
-
options.sleep ??
|
|
128
|
-
((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
129
|
-
this.classifyError = options.classifyError ?? (() => null);
|
|
130
|
-
this.lastIncreaseAtMs = this.now();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
async dispatch(
|
|
134
|
-
jobs: readonly TJob[],
|
|
135
|
-
execute: (job: TJob) => Promise<TResult>,
|
|
136
|
-
): Promise<Array<AdaptiveToolDispatchResult<TJob, TResult>>> {
|
|
137
|
-
const queue = jobs.map((job, index) => ({ job, index }));
|
|
138
|
-
const results = new Array<AdaptiveToolDispatchResult<TJob, TResult>>(
|
|
139
|
-
jobs.length,
|
|
140
|
-
);
|
|
141
|
-
this.queued = queue.length;
|
|
142
|
-
|
|
143
|
-
while (queue.length > 0 || this.inFlight > 0) {
|
|
144
|
-
let launched = false;
|
|
145
|
-
while (queue.length > 0 && this.canLaunch()) {
|
|
146
|
-
const next = queue.shift();
|
|
147
|
-
if (!next) break;
|
|
148
|
-
this.launch(next, execute, results);
|
|
149
|
-
launched = true;
|
|
150
|
-
}
|
|
151
|
-
this.queued = queue.length;
|
|
152
|
-
if (queue.length === 0 && this.inFlight === 0) break;
|
|
153
|
-
if (!launched) {
|
|
154
|
-
await this.waitForProgress(this.nextLaunchDelayMs());
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return results.filter(
|
|
159
|
-
(result): result is AdaptiveToolDispatchResult<TJob, TResult> =>
|
|
160
|
-
result !== undefined,
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
snapshot(): AdaptiveToolDispatcherSnapshot {
|
|
165
|
-
return {
|
|
166
|
-
currentRequestsPerSecond: this.currentRequestsPerSecond,
|
|
167
|
-
targetConcurrency: this.targetConcurrency(),
|
|
168
|
-
inFlight: this.inFlight,
|
|
169
|
-
queued: this.queued,
|
|
170
|
-
maxObservedInFlight: this.maxObservedInFlight,
|
|
171
|
-
successCount: this.successCount,
|
|
172
|
-
errorCount: this.errorCount,
|
|
173
|
-
rateLimitCount: this.rateLimitCount,
|
|
174
|
-
latencyP95Ms: this.latencyP95Ms(),
|
|
175
|
-
cooldownUntilMs: this.cooldownUntilMs,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
private canLaunch(): boolean {
|
|
180
|
-
return (
|
|
181
|
-
this.inFlight < this.targetConcurrency() &&
|
|
182
|
-
this.now() >= (this.cooldownUntilMs ?? 0) &&
|
|
183
|
-
this.startsAvailable() > 0
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
private launch(
|
|
188
|
-
queued: QueuedJob<TJob>,
|
|
189
|
-
execute: (job: TJob) => Promise<TResult>,
|
|
190
|
-
results: Array<AdaptiveToolDispatchResult<TJob, TResult> | undefined>,
|
|
191
|
-
): void {
|
|
192
|
-
const startedAt = this.now();
|
|
193
|
-
this.launchTimestamps.push(startedAt);
|
|
194
|
-
this.inFlight += 1;
|
|
195
|
-
this.maxObservedInFlight = Math.max(
|
|
196
|
-
this.maxObservedInFlight,
|
|
197
|
-
this.inFlight,
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
let execution: Promise<TResult>;
|
|
201
|
-
try {
|
|
202
|
-
execution = Promise.resolve(execute(queued.job));
|
|
203
|
-
} catch (error) {
|
|
204
|
-
execution = Promise.reject(error);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
void execution
|
|
208
|
-
.then((result) => {
|
|
209
|
-
const latencyMs = Math.max(0, this.now() - startedAt);
|
|
210
|
-
this.observeSuccess(latencyMs);
|
|
211
|
-
results[queued.index] = {
|
|
212
|
-
job: queued.job,
|
|
213
|
-
index: queued.index,
|
|
214
|
-
result,
|
|
215
|
-
latencyMs,
|
|
216
|
-
};
|
|
217
|
-
})
|
|
218
|
-
.catch((error) => {
|
|
219
|
-
this.observeError(error);
|
|
220
|
-
results[queued.index] = {
|
|
221
|
-
job: queued.job,
|
|
222
|
-
index: queued.index,
|
|
223
|
-
result: null,
|
|
224
|
-
latencyMs: Math.max(0, this.now() - startedAt),
|
|
225
|
-
error,
|
|
226
|
-
};
|
|
227
|
-
})
|
|
228
|
-
.finally(() => {
|
|
229
|
-
this.inFlight = Math.max(0, this.inFlight - 1);
|
|
230
|
-
this.signalProgress();
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
private observeSuccess(latencyMs: number): void {
|
|
235
|
-
this.successCount += 1;
|
|
236
|
-
this.latencySamples.push(latencyMs);
|
|
237
|
-
if (this.latencySamples.length > 128) {
|
|
238
|
-
this.latencySamples.splice(0, this.latencySamples.length - 128);
|
|
239
|
-
}
|
|
240
|
-
const currentTime = this.now();
|
|
241
|
-
if (this.cooldownUntilMs != null && this.cooldownUntilMs > currentTime) {
|
|
242
|
-
this.successCountAtLastIncrease = this.successCount;
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
this.cooldownUntilMs = null;
|
|
246
|
-
const successesSinceIncrease =
|
|
247
|
-
this.successCount - this.successCountAtLastIncrease;
|
|
248
|
-
if (
|
|
249
|
-
currentTime - this.lastIncreaseAtMs >= this.cleanWindowMs &&
|
|
250
|
-
successesSinceIncrease >= this.currentRequestsPerSecond
|
|
251
|
-
) {
|
|
252
|
-
this.currentRequestsPerSecond = Math.min(
|
|
253
|
-
this.maxRequestsPerSecond,
|
|
254
|
-
this.currentRequestsPerSecond + this.additiveIncreasePerWindow,
|
|
255
|
-
);
|
|
256
|
-
this.lastIncreaseAtMs = currentTime;
|
|
257
|
-
this.successCountAtLastIncrease = this.successCount;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
private observeError(error: unknown): void {
|
|
262
|
-
this.errorCount += 1;
|
|
263
|
-
let classified: AdaptiveToolDispatchError | null = null;
|
|
264
|
-
try {
|
|
265
|
-
classified = this.classifyError(error);
|
|
266
|
-
} catch {
|
|
267
|
-
classified = null;
|
|
268
|
-
}
|
|
269
|
-
if (classified?.kind !== 'rate_limit') return;
|
|
270
|
-
this.rateLimitCount += 1;
|
|
271
|
-
this.currentRequestsPerSecond = Math.max(
|
|
272
|
-
1,
|
|
273
|
-
Math.floor(this.currentRequestsPerSecond / 2),
|
|
274
|
-
);
|
|
275
|
-
const retryAfterMs = Math.max(0, classified.retryAfterMs ?? 0);
|
|
276
|
-
const currentTime = this.now();
|
|
277
|
-
const cooldownUntilMs = currentTime + retryAfterMs;
|
|
278
|
-
this.cooldownUntilMs =
|
|
279
|
-
this.cooldownUntilMs == null
|
|
280
|
-
? cooldownUntilMs
|
|
281
|
-
: Math.max(this.cooldownUntilMs, cooldownUntilMs);
|
|
282
|
-
this.lastIncreaseAtMs = this.cooldownUntilMs;
|
|
283
|
-
this.successCountAtLastIncrease = this.successCount;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
private targetConcurrency(): number {
|
|
287
|
-
const latencyMs = this.latencyP95Ms() ?? this.defaultLatencyMs;
|
|
288
|
-
const target = Math.ceil(
|
|
289
|
-
this.currentRequestsPerSecond *
|
|
290
|
-
(Math.max(1, latencyMs) / 1_000) *
|
|
291
|
-
this.safetyFactor,
|
|
292
|
-
);
|
|
293
|
-
return clamp(target, this.minConcurrency, this.maxConcurrency);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
private latencyP95Ms(): number | null {
|
|
297
|
-
return this.latencySamples.length > 0
|
|
298
|
-
? percentile(this.latencySamples, 0.95)
|
|
299
|
-
: null;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
private startsAvailable(): number {
|
|
303
|
-
this.pruneLaunchTimestamps();
|
|
304
|
-
return Math.max(
|
|
305
|
-
0,
|
|
306
|
-
this.currentRequestsPerSecond - this.launchTimestamps.length,
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
private nextLaunchDelayMs(): number {
|
|
311
|
-
const currentTime = this.now();
|
|
312
|
-
const cooldownWait =
|
|
313
|
-
this.cooldownUntilMs != null
|
|
314
|
-
? Math.max(0, this.cooldownUntilMs - currentTime)
|
|
315
|
-
: 0;
|
|
316
|
-
this.pruneLaunchTimestamps();
|
|
317
|
-
const oldest = this.launchTimestamps[0];
|
|
318
|
-
const tokenWait =
|
|
319
|
-
oldest == null ||
|
|
320
|
-
this.launchTimestamps.length < this.currentRequestsPerSecond
|
|
321
|
-
? 0
|
|
322
|
-
: Math.max(1, oldest + 1_000 - currentTime);
|
|
323
|
-
return Math.max(1, cooldownWait, tokenWait);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
private pruneLaunchTimestamps(): void {
|
|
327
|
-
const cutoff = this.now() - 1_000;
|
|
328
|
-
while (
|
|
329
|
-
this.launchTimestamps.length > 0 &&
|
|
330
|
-
(this.launchTimestamps[0] ?? 0) <= cutoff
|
|
331
|
-
) {
|
|
332
|
-
this.launchTimestamps.shift();
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
private async waitForProgress(delayMs: number): Promise<void> {
|
|
337
|
-
let waiter: (() => void) | null = null;
|
|
338
|
-
await Promise.race([
|
|
339
|
-
new Promise<void>((resolve) => {
|
|
340
|
-
waiter = resolve;
|
|
341
|
-
this.progressWaiters.push(resolve);
|
|
342
|
-
}),
|
|
343
|
-
this.sleep(delayMs),
|
|
344
|
-
]);
|
|
345
|
-
if (waiter) {
|
|
346
|
-
const index = this.progressWaiters.indexOf(waiter);
|
|
347
|
-
if (index >= 0) this.progressWaiters.splice(index, 1);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
private signalProgress(): void {
|
|
352
|
-
const waiters = this.progressWaiters.splice(0);
|
|
353
|
-
for (const waiter of waiters) waiter();
|
|
354
|
-
}
|
|
355
|
-
}
|