@allurereport/plugin-testops 3.16.0 → 3.16.1
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/README.md +2 -0
- package/dist/client.d.ts +7 -3
- package/dist/client.js +169 -147
- package/dist/errors.d.ts +23 -0
- package/dist/errors.js +134 -0
- package/dist/gitFlow/options.d.ts +1 -0
- package/dist/gitFlow/options.js +1 -1
- package/dist/model.d.ts +26 -1
- package/dist/plugin.js +127 -141
- package/dist/uploadPacer.d.ts +12 -0
- package/dist/uploadPacer.js +76 -0
- package/dist/uploadQueue.d.ts +15 -0
- package/dist/uploadQueue.js +61 -0
- package/dist/utils/ciOverrides.d.ts +2 -0
- package/dist/utils/ciOverrides.js +20 -0
- package/dist/utils/httpRetry.d.ts +4 -0
- package/dist/utils/httpRetry.js +62 -0
- package/dist/utils/jobParameters.d.ts +15 -0
- package/dist/utils/jobParameters.js +39 -0
- package/dist/utils/launchCategories.d.ts +7 -0
- package/dist/utils/launchCategories.js +97 -0
- package/dist/utils/options.d.ts +1 -0
- package/dist/utils/options.js +14 -2
- package/dist/utils/resolvers.js +1 -0
- package/dist/utils/testResults.js +0 -1
- package/package.json +8 -8
package/dist/plugin.js
CHANGED
|
@@ -9,31 +9,34 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _TestOpsPlugin_instances, _TestOpsPlugin_logger, _TestOpsPlugin_ci, _TestOpsPlugin_client, _TestOpsPlugin_launchName, _TestOpsPlugin_launchTags, _TestOpsPlugin_uploadedTestResultsIds, _TestOpsPlugin_uploadedGlobalAttachmentIds, _TestOpsPlugin_uploadedGlobalErrors, _TestOpsPlugin_autocloseLaunch, _TestOpsPlugin_launchStarted, _TestOpsPlugin_gitFlow, _TestOpsPlugin_enabledByConfig,
|
|
12
|
+
var _TestOpsPlugin_instances, _TestOpsPlugin_logger, _TestOpsPlugin_ci, _TestOpsPlugin_client, _TestOpsPlugin_launchName, _TestOpsPlugin_launchTags, _TestOpsPlugin_uploadedTestResultsIds, _TestOpsPlugin_uploadedGlobalAttachmentIds, _TestOpsPlugin_uploadedGlobalErrors, _TestOpsPlugin_autocloseLaunch, _TestOpsPlugin_launchStarted, _TestOpsPlugin_reopenClosedLaunch, _TestOpsPlugin_skippedUploadCycle, _TestOpsPlugin_launchId, _TestOpsPlugin_gitFlow, _TestOpsPlugin_enabledByConfig, _TestOpsPlugin_uploadQueue, _TestOpsPlugin_reopenLaunchIfClosed, _TestOpsPlugin_logDeferredUpload, _TestOpsPlugin_retryHooks, _TestOpsPlugin_logUploadFailure, _TestOpsPlugin_uploadQualityGateResults, _TestOpsPlugin_uploadGlobalErrors, _TestOpsPlugin_uploadGlobalAttachments, _TestOpsPlugin_uploadTestResults, _TestOpsPlugin_upload, _TestOpsPlugin_trsToUpload, _TestOpsPlugin_startUpload, _TestOpsPlugin_stopUpload;
|
|
13
13
|
import { env } from "node:process";
|
|
14
|
-
import { detect, isLocalCiDescriptor } from "@allurereport/ci";
|
|
14
|
+
import { applyAllureCiEnv, detect, isLocalCiDescriptor } from "@allurereport/ci";
|
|
15
15
|
import { createProgressLogger } from "@allurereport/cli-commons";
|
|
16
16
|
import { getWorstStatus } from "@allurereport/core-api";
|
|
17
17
|
import { createPluginSummary, } from "@allurereport/plugin-api";
|
|
18
18
|
import { uniqBy, stubTrue } from "lodash-es";
|
|
19
19
|
import { bold } from "yoctocolors";
|
|
20
20
|
import { TestOpsClient } from "./client.js";
|
|
21
|
+
import { isClosedLaunchError } from "./errors.js";
|
|
21
22
|
import { LaunchGitFlow, resolveGitFlowOptions } from "./gitFlow/index.js";
|
|
22
23
|
import { Logger } from "./logger.js";
|
|
24
|
+
import { UploadQueue } from "./uploadQueue.js";
|
|
23
25
|
import { uploadFilenameForLink } from "./utils/attachments.js";
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
26
|
+
import { applyCiOverrides } from "./utils/ciOverrides.js";
|
|
27
|
+
import { enrichWithCategories, syncLaunchCategories } from "./utils/launchCategories.js";
|
|
28
|
+
import { resolvePluginOptions, toPositiveInteger } from "./utils/options.js";
|
|
29
|
+
import { attachmentsResolverFactory, fixturesResolverFactory } from "./utils/resolvers.js";
|
|
27
30
|
import { validateExecutableName } from "./utils/validation.js";
|
|
28
31
|
const LAUNCH_PROGRESS_POLL_DELAY_MS = 500;
|
|
29
32
|
const LAUNCH_PROGRESS_ATTEMPTS_LIMIT = 10;
|
|
30
|
-
const
|
|
33
|
+
const resolveJobRunIdFromEnv = () => toPositiveInteger(env.ALLURE_JOB_RUN_ID);
|
|
31
34
|
export class TestOpsPlugin {
|
|
32
35
|
constructor(options, context = {}) {
|
|
33
36
|
_TestOpsPlugin_instances.add(this);
|
|
34
37
|
this.options = options;
|
|
35
38
|
_TestOpsPlugin_logger.set(this, new Logger("TestOpsPlugin"));
|
|
36
|
-
_TestOpsPlugin_ci.set(this,
|
|
39
|
+
_TestOpsPlugin_ci.set(this, void 0);
|
|
37
40
|
_TestOpsPlugin_client.set(this, void 0);
|
|
38
41
|
_TestOpsPlugin_launchName.set(this, "");
|
|
39
42
|
_TestOpsPlugin_launchTags.set(this, []);
|
|
@@ -42,8 +45,14 @@ export class TestOpsPlugin {
|
|
|
42
45
|
_TestOpsPlugin_uploadedGlobalErrors.set(this, new Set());
|
|
43
46
|
_TestOpsPlugin_autocloseLaunch.set(this, false);
|
|
44
47
|
_TestOpsPlugin_launchStarted.set(this, false);
|
|
48
|
+
_TestOpsPlugin_reopenClosedLaunch.set(this, false);
|
|
49
|
+
_TestOpsPlugin_skippedUploadCycle.set(this, false);
|
|
50
|
+
_TestOpsPlugin_launchId.set(this, void 0);
|
|
45
51
|
_TestOpsPlugin_gitFlow.set(this, void 0);
|
|
46
52
|
_TestOpsPlugin_enabledByConfig.set(this, false);
|
|
53
|
+
_TestOpsPlugin_uploadQueue.set(this, new UploadQueue());
|
|
54
|
+
applyAllureCiEnv();
|
|
55
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_ci, applyCiOverrides(detect()), "f");
|
|
47
56
|
__classPrivateFieldSet(this, _TestOpsPlugin_enabledByConfig, context.enabled === true, "f");
|
|
48
57
|
if (context.enabled === false) {
|
|
49
58
|
return;
|
|
@@ -52,17 +61,20 @@ export class TestOpsPlugin {
|
|
|
52
61
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info(`plugin is disabled - no CI environment detected. To enable, set ${bold("ALLURE_TESTOPS_ENABLED")}=true or ${bold("CI")}=true.`);
|
|
53
62
|
return;
|
|
54
63
|
}
|
|
55
|
-
const { accessToken, endpoint, projectId, launchName, launchTags, autocloseLaunch = true, } = resolvePluginOptions(options);
|
|
64
|
+
const { accessToken, endpoint, projectId, launchName, launchTags, autocloseLaunch = true, uploadRateLimit, reopenClosedLaunch = false, launchId, } = resolvePluginOptions(options);
|
|
56
65
|
if ([accessToken, endpoint, projectId].every(Boolean)) {
|
|
57
66
|
__classPrivateFieldSet(this, _TestOpsPlugin_client, new TestOpsClient({
|
|
58
67
|
baseUrl: endpoint,
|
|
59
68
|
accessToken,
|
|
60
69
|
projectId,
|
|
70
|
+
uploadRateLimit,
|
|
61
71
|
}), "f");
|
|
62
72
|
__classPrivateFieldSet(this, _TestOpsPlugin_launchName, launchName, "f");
|
|
63
73
|
__classPrivateFieldSet(this, _TestOpsPlugin_launchTags, launchTags, "f");
|
|
64
74
|
}
|
|
65
75
|
__classPrivateFieldSet(this, _TestOpsPlugin_autocloseLaunch, autocloseLaunch, "f");
|
|
76
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_reopenClosedLaunch, reopenClosedLaunch, "f");
|
|
77
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_launchId, launchId, "f");
|
|
66
78
|
const gitFlowOptions = resolveGitFlowOptions(options);
|
|
67
79
|
__classPrivateFieldSet(this, _TestOpsPlugin_gitFlow, new LaunchGitFlow({
|
|
68
80
|
ci: __classPrivateFieldGet(this, _TestOpsPlugin_ci, "f"),
|
|
@@ -87,7 +99,7 @@ export class TestOpsPlugin {
|
|
|
87
99
|
}
|
|
88
100
|
return ["true", "1"].includes(value);
|
|
89
101
|
};
|
|
90
|
-
return isEnabled(env.ALLURE_TESTOPS_ENABLED) || isEnabled(env.CI);
|
|
102
|
+
return isEnabled(env.ALLURE_TESTOPS_ENABLED) || isEnabled(env.CI) || resolveJobRunIdFromEnv() !== undefined;
|
|
91
103
|
}
|
|
92
104
|
get isManuallyEnabled() {
|
|
93
105
|
return __classPrivateFieldGet(this, _TestOpsPlugin_enabledByConfig, "f") || this.isOverridenByEnv;
|
|
@@ -144,6 +156,15 @@ export class TestOpsPlugin {
|
|
|
144
156
|
const worstStatus = getWorstStatus(allTrs.map(({ status }) => status));
|
|
145
157
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("Finalizing upload…");
|
|
146
158
|
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_upload).call(this, store, { context, stage: "done" });
|
|
159
|
+
const stillPending = await __classPrivateFieldGet(this, _TestOpsPlugin_uploadQueue, "f").flush({
|
|
160
|
+
onRetry: async (error, attempt) => {
|
|
161
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(`Retrying deferred TestOps upload at finalization (attempt ${attempt}): ${error}`);
|
|
162
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_reopenLaunchIfClosed).call(this, error);
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
if (stillPending.length > 0) {
|
|
166
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`${stillPending.length} TestOps upload(s) could not be completed after retrying at finalization: ${stillPending.join(", ")}`);
|
|
167
|
+
}
|
|
147
168
|
try {
|
|
148
169
|
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_stopUpload).call(this, worstStatus || "unknown");
|
|
149
170
|
}
|
|
@@ -167,6 +188,10 @@ export class TestOpsPlugin {
|
|
|
167
188
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info(`Upload finished. Allure TestOps Launch: ${__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").launchUrl}`);
|
|
168
189
|
return;
|
|
169
190
|
}
|
|
191
|
+
if (stillPending.length > 0 || __classPrivateFieldGet(this, _TestOpsPlugin_skippedUploadCycle, "f")) {
|
|
192
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").warn(`Not closing launch ${launchId}: some uploads never made it to TestOps, closing now would report an incomplete launch as finished.`);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
170
195
|
let launchIsReady = false;
|
|
171
196
|
for (let attempt = 0; attempt < LAUNCH_PROGRESS_ATTEMPTS_LIMIT; attempt += 1) {
|
|
172
197
|
launchIsReady = await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").checkLaunchProgress();
|
|
@@ -214,7 +239,45 @@ export class TestOpsPlugin {
|
|
|
214
239
|
return summary;
|
|
215
240
|
}
|
|
216
241
|
}
|
|
217
|
-
_TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestOpsPlugin_client = new WeakMap(), _TestOpsPlugin_launchName = new WeakMap(), _TestOpsPlugin_launchTags = new WeakMap(), _TestOpsPlugin_uploadedTestResultsIds = new WeakMap(), _TestOpsPlugin_uploadedGlobalAttachmentIds = new WeakMap(), _TestOpsPlugin_uploadedGlobalErrors = new WeakMap(), _TestOpsPlugin_autocloseLaunch = new WeakMap(), _TestOpsPlugin_launchStarted = new WeakMap(), _TestOpsPlugin_gitFlow = new WeakMap(), _TestOpsPlugin_enabledByConfig = new WeakMap(), _TestOpsPlugin_instances = new WeakSet(),
|
|
242
|
+
_TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestOpsPlugin_client = new WeakMap(), _TestOpsPlugin_launchName = new WeakMap(), _TestOpsPlugin_launchTags = new WeakMap(), _TestOpsPlugin_uploadedTestResultsIds = new WeakMap(), _TestOpsPlugin_uploadedGlobalAttachmentIds = new WeakMap(), _TestOpsPlugin_uploadedGlobalErrors = new WeakMap(), _TestOpsPlugin_autocloseLaunch = new WeakMap(), _TestOpsPlugin_launchStarted = new WeakMap(), _TestOpsPlugin_reopenClosedLaunch = new WeakMap(), _TestOpsPlugin_skippedUploadCycle = new WeakMap(), _TestOpsPlugin_launchId = new WeakMap(), _TestOpsPlugin_gitFlow = new WeakMap(), _TestOpsPlugin_enabledByConfig = new WeakMap(), _TestOpsPlugin_uploadQueue = new WeakMap(), _TestOpsPlugin_instances = new WeakSet(), _TestOpsPlugin_reopenLaunchIfClosed = async function _TestOpsPlugin_reopenLaunchIfClosed(error) {
|
|
243
|
+
if (!__classPrivateFieldGet(this, _TestOpsPlugin_reopenClosedLaunch, "f") || !isClosedLaunchError(error)) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const launchId = __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").launchId;
|
|
247
|
+
if (launchId === undefined) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
try {
|
|
251
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").warn(`Launch ${launchId} was closed - reopening before retrying the upload…`);
|
|
252
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").reopenLaunch(launchId);
|
|
253
|
+
}
|
|
254
|
+
catch (reopenError) {
|
|
255
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(`Failed to reopen launch ${launchId}: ${reopenError}`);
|
|
256
|
+
}
|
|
257
|
+
}, _TestOpsPlugin_logDeferredUpload = function _TestOpsPlugin_logDeferredUpload(label, error) {
|
|
258
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").warn(error
|
|
259
|
+
? `TestOps upload is unavailable; ${label} will be retried during finalization. Reason: ${error}`
|
|
260
|
+
: `TestOps upload is suspended; ${label} will be retried during finalization.`);
|
|
261
|
+
}, _TestOpsPlugin_retryHooks = function _TestOpsPlugin_retryHooks(label) {
|
|
262
|
+
return {
|
|
263
|
+
onRetry: async (error, attempt) => {
|
|
264
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(`Retrying ${label} upload (attempt ${attempt}): ${error}`);
|
|
265
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_reopenLaunchIfClosed).call(this, error);
|
|
266
|
+
},
|
|
267
|
+
onDeferred: (error) => __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_logDeferredUpload).call(this, label, error),
|
|
268
|
+
};
|
|
269
|
+
}, _TestOpsPlugin_logUploadFailure = function _TestOpsPlugin_logUploadFailure(label, error) {
|
|
270
|
+
if (__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").isTestOpsClientError(error)) {
|
|
271
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload ${label}: ${error.response.data.message}`);
|
|
272
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
273
|
+
}
|
|
274
|
+
else if (error instanceof Error) {
|
|
275
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload ${label}: ${error.message}`);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload ${label}`);
|
|
279
|
+
}
|
|
280
|
+
}, _TestOpsPlugin_uploadQualityGateResults = async function _TestOpsPlugin_uploadQualityGateResults(store) {
|
|
218
281
|
const results = await store.qualityGateResults();
|
|
219
282
|
const uniqueResults = uniqBy(results.filter(({ success }) => !success), ({ rule, environment }) => `${rule}-${environment}`);
|
|
220
283
|
if (uniqueResults.length === 0) {
|
|
@@ -230,28 +293,22 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
230
293
|
let completed = false;
|
|
231
294
|
try {
|
|
232
295
|
progressLogger.log(true);
|
|
233
|
-
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadQualityGateResults(uniqueResults, (percent) => {
|
|
296
|
+
const result = await __classPrivateFieldGet(this, _TestOpsPlugin_uploadQueue, "f").run("quality-gate", () => __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadQualityGateResults(uniqueResults, (percent) => {
|
|
234
297
|
if (!completed && percent >= 100) {
|
|
235
298
|
completed = true;
|
|
236
299
|
progressLogger.increment();
|
|
237
300
|
}
|
|
238
|
-
});
|
|
301
|
+
}), __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_retryHooks).call(this, "quality gate results"));
|
|
302
|
+
if (result.deferred) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
239
305
|
if (!completed) {
|
|
240
306
|
progressLogger.increment();
|
|
241
307
|
}
|
|
242
308
|
progressLogger.log(true);
|
|
243
309
|
}
|
|
244
310
|
catch (error) {
|
|
245
|
-
|
|
246
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload quality gate results: ${error.response.data.message}`);
|
|
247
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
248
|
-
}
|
|
249
|
-
else if (error instanceof Error) {
|
|
250
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload quality gate results: ${error.message}`);
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error("Failed to upload quality gate results");
|
|
254
|
-
}
|
|
311
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_logUploadFailure).call(this, "quality gate results", error);
|
|
255
312
|
}
|
|
256
313
|
finally {
|
|
257
314
|
progressLogger.cancel?.();
|
|
@@ -270,12 +327,15 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
270
327
|
let completed = false;
|
|
271
328
|
try {
|
|
272
329
|
progressLogger.log(true);
|
|
273
|
-
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadGlobalErrors(results, (percent) => {
|
|
330
|
+
const result = await __classPrivateFieldGet(this, _TestOpsPlugin_uploadQueue, "f").run("global-errors", () => __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadGlobalErrors(results, (percent) => {
|
|
274
331
|
if (!completed && percent >= 100) {
|
|
275
332
|
completed = true;
|
|
276
333
|
progressLogger.increment();
|
|
277
334
|
}
|
|
278
|
-
});
|
|
335
|
+
}), __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_retryHooks).call(this, "global errors"));
|
|
336
|
+
if (result.deferred) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
279
339
|
if (!completed) {
|
|
280
340
|
progressLogger.increment();
|
|
281
341
|
}
|
|
@@ -285,16 +345,7 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
285
345
|
progressLogger.log(true);
|
|
286
346
|
}
|
|
287
347
|
catch (error) {
|
|
288
|
-
|
|
289
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload global errors: ${error.response.data.message}`);
|
|
290
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
291
|
-
}
|
|
292
|
-
else if (error instanceof Error) {
|
|
293
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload global errors: ${error.message}`);
|
|
294
|
-
}
|
|
295
|
-
else {
|
|
296
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error("Failed to upload global errors");
|
|
297
|
-
}
|
|
348
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_logUploadFailure).call(this, "global errors", error);
|
|
298
349
|
}
|
|
299
350
|
finally {
|
|
300
351
|
progressLogger.cancel?.();
|
|
@@ -313,7 +364,7 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
313
364
|
let completed = false;
|
|
314
365
|
try {
|
|
315
366
|
progressLogger.log(true);
|
|
316
|
-
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadGlobalAttachments({
|
|
367
|
+
const result = await __classPrivateFieldGet(this, _TestOpsPlugin_uploadQueue, "f").run("global-attachments", () => __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadGlobalAttachments({
|
|
317
368
|
attachments,
|
|
318
369
|
attachmentsResolver: async (attachmentLink) => {
|
|
319
370
|
const content = await store.attachmentContentById(attachmentLink.id);
|
|
@@ -326,6 +377,7 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
326
377
|
originalFileName: filename,
|
|
327
378
|
contentType: attachmentLink.contentType ?? "application/octet-stream",
|
|
328
379
|
content: body,
|
|
380
|
+
contentLength: content?.getContentLength(),
|
|
329
381
|
};
|
|
330
382
|
},
|
|
331
383
|
onProgress: (percent) => {
|
|
@@ -334,7 +386,10 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
334
386
|
progressLogger.increment();
|
|
335
387
|
}
|
|
336
388
|
},
|
|
337
|
-
});
|
|
389
|
+
}), __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_retryHooks).call(this, "global attachments"));
|
|
390
|
+
if (result.deferred) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
338
393
|
if (!completed) {
|
|
339
394
|
progressLogger.increment();
|
|
340
395
|
}
|
|
@@ -344,16 +399,7 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
344
399
|
progressLogger.log(true);
|
|
345
400
|
}
|
|
346
401
|
catch (error) {
|
|
347
|
-
|
|
348
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload global attachments: ${error.response.data.message}`);
|
|
349
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
350
|
-
}
|
|
351
|
-
else if (error instanceof Error) {
|
|
352
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload global attachments: ${error.message}`);
|
|
353
|
-
}
|
|
354
|
-
else {
|
|
355
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error("Failed to upload global attachments");
|
|
356
|
-
}
|
|
402
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_logUploadFailure).call(this, "global attachments", error);
|
|
357
403
|
}
|
|
358
404
|
finally {
|
|
359
405
|
progressLogger.cancel?.();
|
|
@@ -370,13 +416,22 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
370
416
|
});
|
|
371
417
|
try {
|
|
372
418
|
progressLogger.log(true);
|
|
373
|
-
const
|
|
419
|
+
const { onRetry, onDeferred } = __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_retryHooks).call(this, "test results");
|
|
420
|
+
const result = await __classPrivateFieldGet(this, _TestOpsPlugin_uploadQueue, "f").run("test-results", () => __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").uploadTestResults({
|
|
374
421
|
attachmentsResolver: attachmentsResolverFactory(store),
|
|
375
422
|
fixturesResolver: fixturesResolverFactory(store),
|
|
376
423
|
environments,
|
|
377
|
-
trs: trsToUpload,
|
|
424
|
+
trs: trsToUpload.filter((tr) => !__classPrivateFieldGet(this, _TestOpsPlugin_uploadedTestResultsIds, "f").has(tr.id)),
|
|
378
425
|
onProgress: () => progressLogger.increment(),
|
|
379
|
-
|
|
426
|
+
onRetry,
|
|
427
|
+
onChunkUploaded: (uploadedChunkTrs) => {
|
|
428
|
+
uploadedChunkTrs.forEach((tr) => __classPrivateFieldGet(this, _TestOpsPlugin_uploadedTestResultsIds, "f").add(tr.id));
|
|
429
|
+
},
|
|
430
|
+
}), { onRetry, onDeferred });
|
|
431
|
+
if (result.deferred) {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
const uploadedTrs = result.value;
|
|
380
435
|
progressLogger.log(true);
|
|
381
436
|
uploadedTrs.forEach((tr) => {
|
|
382
437
|
__classPrivateFieldGet(this, _TestOpsPlugin_uploadedTestResultsIds, "f").add(tr.id);
|
|
@@ -386,7 +441,10 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
386
441
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("No test results were uploaded");
|
|
387
442
|
return;
|
|
388
443
|
}
|
|
389
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").
|
|
444
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info(`Uploaded ${uploadedCount} ${uploadedCount > 1 ? "test results" : "test result"}`);
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_logUploadFailure).call(this, "test results", error);
|
|
390
448
|
}
|
|
391
449
|
finally {
|
|
392
450
|
progressLogger.cancel?.();
|
|
@@ -420,9 +478,10 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
420
478
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose(`Found ${bold(trsToUpload.length.toString())} new test ${trsToUpload.length > 1 ? "results" : "result"}, uploading…`);
|
|
421
479
|
}
|
|
422
480
|
try {
|
|
423
|
-
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").createSession(env);
|
|
481
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").createSession(env, __classPrivateFieldGet(this, _TestOpsPlugin_reopenClosedLaunch, "f"));
|
|
424
482
|
}
|
|
425
483
|
catch (error) {
|
|
484
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_skippedUploadCycle, true, "f");
|
|
426
485
|
if (__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").isTestOpsClientError(error)) {
|
|
427
486
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to create TestOps session: ${error.response.data.message}`);
|
|
428
487
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
@@ -435,6 +494,7 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
435
494
|
}
|
|
436
495
|
return;
|
|
437
496
|
}
|
|
497
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_skippedUploadCycle, false, "f");
|
|
438
498
|
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_uploadGlobalAttachments).call(this, store, globalAttachments);
|
|
439
499
|
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_uploadGlobalErrors).call(this, globalErrors);
|
|
440
500
|
if (trsToUpload.length === 0) {
|
|
@@ -443,8 +503,11 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
443
503
|
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_uploadQualityGateResults).call(this, store);
|
|
444
504
|
const environments = await store.allEnvironmentIdentities();
|
|
445
505
|
const contextCategories = context?.categories ?? [];
|
|
446
|
-
const trsEnrichedWithCategories = await
|
|
447
|
-
await __classPrivateFieldGet(this,
|
|
506
|
+
const trsEnrichedWithCategories = await enrichWithCategories(store, trsToUpload, contextCategories);
|
|
507
|
+
const categoriesError = await syncLaunchCategories(__classPrivateFieldGet(this, _TestOpsPlugin_client, "f"), trsEnrichedWithCategories, contextCategories);
|
|
508
|
+
if (categoriesError) {
|
|
509
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").warn(`Uploading test results without launch categories: ${categoriesError.message}`);
|
|
510
|
+
}
|
|
448
511
|
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_uploadTestResults).call(this, store, trsEnrichedWithCategories, environments, {
|
|
449
512
|
silent: !!context?.realTime,
|
|
450
513
|
});
|
|
@@ -461,96 +524,19 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
461
524
|
includeRetries: false,
|
|
462
525
|
});
|
|
463
526
|
return filteredTrs;
|
|
464
|
-
}, _TestOpsPlugin_enrichWithCategories = async function _TestOpsPlugin_enrichWithCategories(store, trs, contextCategories) {
|
|
465
|
-
return Promise.all(trs.map(async (tr) => {
|
|
466
|
-
const environmentId = await store.environmentIdByTrId(tr.id);
|
|
467
|
-
const base = {
|
|
468
|
-
...tr,
|
|
469
|
-
...(environmentId ? { environment: environmentId } : {}),
|
|
470
|
-
steps: unwrapStepsAttachments(tr.steps),
|
|
471
|
-
};
|
|
472
|
-
const category = toUploadCategory(base, contextCategories ?? []);
|
|
473
|
-
if (category) {
|
|
474
|
-
base.category = category;
|
|
475
|
-
}
|
|
476
|
-
return base;
|
|
477
|
-
}));
|
|
478
|
-
}, _TestOpsPlugin_syncLaunchCategories = async function _TestOpsPlugin_syncLaunchCategories(trs, contextCategories) {
|
|
479
|
-
const categoryNamesByExternalId = __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_collectCategoryNamesByExternalId).call(this, trs);
|
|
480
|
-
if (categoryNamesByExternalId.size === 0) {
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
const bulkItems = [];
|
|
484
|
-
const seenExternalIds = new Set();
|
|
485
|
-
for (const tr of trs) {
|
|
486
|
-
const cat = tr.category;
|
|
487
|
-
if (!cat?.externalId)
|
|
488
|
-
continue;
|
|
489
|
-
if (seenExternalIds.has(cat.externalId))
|
|
490
|
-
continue;
|
|
491
|
-
seenExternalIds.add(cat.externalId);
|
|
492
|
-
bulkItems.push({
|
|
493
|
-
externalId: cat.externalId,
|
|
494
|
-
name: categoryNamesByExternalId.get(cat.externalId) ?? categoryDisplayName(cat),
|
|
495
|
-
hide: cat.hide,
|
|
496
|
-
expand: cat.expand,
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
const rankByExternalId = new Map();
|
|
500
|
-
for (const c of contextCategories) {
|
|
501
|
-
if (!rankByExternalId.has(c.id)) {
|
|
502
|
-
rankByExternalId.set(c.id, c.index);
|
|
503
|
-
}
|
|
504
|
-
if (!rankByExternalId.has(c.name)) {
|
|
505
|
-
rankByExternalId.set(c.name, c.index);
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
const ranked = bulkItems.map((item, i) => ({
|
|
509
|
-
item,
|
|
510
|
-
i,
|
|
511
|
-
rank: rankByExternalId.get(item.externalId),
|
|
512
|
-
}));
|
|
513
|
-
ranked.sort((a, b) => {
|
|
514
|
-
const ar = a.rank ?? Number.POSITIVE_INFINITY;
|
|
515
|
-
const br = b.rank ?? Number.POSITIVE_INFINITY;
|
|
516
|
-
if (ar !== br)
|
|
517
|
-
return ar - br;
|
|
518
|
-
return a.i - b.i;
|
|
519
|
-
});
|
|
520
|
-
const orderedBulkItems = ranked.map((r) => r.item);
|
|
521
|
-
const launchId = __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").launchId;
|
|
522
|
-
try {
|
|
523
|
-
const created = await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").createLaunchCategoriesBulk(launchId, orderedBulkItems);
|
|
524
|
-
const categoryIdByExternalId = new Map(created.map((r) => [r.externalId, r.id]));
|
|
525
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_assignCreatedCategoryIds).call(this, trs, categoryIdByExternalId);
|
|
526
|
-
}
|
|
527
|
-
catch {
|
|
528
|
-
}
|
|
529
|
-
}, _TestOpsPlugin_collectCategoryNamesByExternalId = function _TestOpsPlugin_collectCategoryNamesByExternalId(trs) {
|
|
530
|
-
const map = new Map();
|
|
531
|
-
for (const tr of trs) {
|
|
532
|
-
const cat = tr.category;
|
|
533
|
-
if (cat?.externalId) {
|
|
534
|
-
map.set(cat.externalId, categoryDisplayName(cat));
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
return map;
|
|
538
|
-
}, _TestOpsPlugin_assignCreatedCategoryIds = function _TestOpsPlugin_assignCreatedCategoryIds(trs, idByExternalId) {
|
|
539
|
-
for (const tr of trs) {
|
|
540
|
-
const cat = tr.category;
|
|
541
|
-
if (!cat?.externalId) {
|
|
542
|
-
continue;
|
|
543
|
-
}
|
|
544
|
-
const id = idByExternalId.get(cat.externalId);
|
|
545
|
-
if (typeof id === "number") {
|
|
546
|
-
tr.category = { ...cat, id };
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
527
|
}, _TestOpsPlugin_startUpload = async function _TestOpsPlugin_startUpload() {
|
|
550
|
-
const
|
|
528
|
+
const jobRunId = resolveJobRunIdFromEnv();
|
|
551
529
|
try {
|
|
552
|
-
|
|
553
|
-
|
|
530
|
+
if (!jobRunId) {
|
|
531
|
+
if (__classPrivateFieldGet(this, _TestOpsPlugin_launchId, "f") !== undefined) {
|
|
532
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").attachToLaunch(__classPrivateFieldGet(this, _TestOpsPlugin_launchId, "f"));
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
const launchGitContext = __classPrivateFieldGet(this, _TestOpsPlugin_gitFlow, "f").resolve();
|
|
536
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").createLaunch(__classPrivateFieldGet(this, _TestOpsPlugin_launchName, "f"), __classPrivateFieldGet(this, _TestOpsPlugin_launchTags, "f"), launchGitContext);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").startUpload(__classPrivateFieldGet(this, _TestOpsPlugin_ci, "f"), jobRunId);
|
|
554
540
|
__classPrivateFieldSet(this, _TestOpsPlugin_launchStarted, true, "f");
|
|
555
541
|
}
|
|
556
542
|
catch (error) {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { UploadRateLimit } from "./model.js";
|
|
2
|
+
export type UploadCost = {
|
|
3
|
+
requests?: number;
|
|
4
|
+
files?: number;
|
|
5
|
+
bytes?: number;
|
|
6
|
+
};
|
|
7
|
+
export declare const DEFAULT_UPLOAD_RATE_LIMIT: UploadRateLimit;
|
|
8
|
+
export declare class UploadPacer {
|
|
9
|
+
#private;
|
|
10
|
+
constructor(rateLimit: UploadRateLimit | false | undefined, now?: () => number);
|
|
11
|
+
wait(cost: UploadCost, signal?: AbortSignal): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _UploadPacer_instances, _UploadPacer_requests, _UploadPacer_files, _UploadPacer_bytes, _UploadPacer_now, _UploadPacer_reserve, _UploadPacer_reserveAt;
|
|
13
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
14
|
+
export const DEFAULT_UPLOAD_RATE_LIMIT = {
|
|
15
|
+
windowMs: 1000,
|
|
16
|
+
maxRequestsPerWindow: 20,
|
|
17
|
+
maxFilesPerWindow: 1000,
|
|
18
|
+
maxBytesPerWindow: 1024 * 1024 * 1024,
|
|
19
|
+
};
|
|
20
|
+
const isBudgetEnabled = (budget) => budget.windowMs > 0 && budget.limit > 0;
|
|
21
|
+
const scaledWindowCost = (windowMs, cost, limit) => {
|
|
22
|
+
if (windowMs <= 0 || cost <= 0 || limit <= 0) {
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
return (windowMs * cost) / limit;
|
|
26
|
+
};
|
|
27
|
+
const resolveRateLimit = (rateLimit) => {
|
|
28
|
+
const rateLimitUnset = rateLimit === undefined;
|
|
29
|
+
const rateLimitDisabled = rateLimit === false;
|
|
30
|
+
if (rateLimitUnset) {
|
|
31
|
+
return DEFAULT_UPLOAD_RATE_LIMIT;
|
|
32
|
+
}
|
|
33
|
+
if (rateLimitDisabled) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
return rateLimit;
|
|
37
|
+
};
|
|
38
|
+
export class UploadPacer {
|
|
39
|
+
constructor(rateLimit, now = Date.now) {
|
|
40
|
+
_UploadPacer_instances.add(this);
|
|
41
|
+
_UploadPacer_requests.set(this, void 0);
|
|
42
|
+
_UploadPacer_files.set(this, void 0);
|
|
43
|
+
_UploadPacer_bytes.set(this, void 0);
|
|
44
|
+
_UploadPacer_now.set(this, void 0);
|
|
45
|
+
const resolvedRateLimit = resolveRateLimit(rateLimit);
|
|
46
|
+
const windowMs = resolvedRateLimit?.windowMs ?? 0;
|
|
47
|
+
__classPrivateFieldSet(this, _UploadPacer_requests, { limit: resolvedRateLimit?.maxRequestsPerWindow ?? 0, windowMs, next: 0 }, "f");
|
|
48
|
+
__classPrivateFieldSet(this, _UploadPacer_files, { limit: resolvedRateLimit?.maxFilesPerWindow ?? 0, windowMs, next: 0 }, "f");
|
|
49
|
+
__classPrivateFieldSet(this, _UploadPacer_bytes, { limit: resolvedRateLimit?.maxBytesPerWindow ?? 0, windowMs, next: 0 }, "f");
|
|
50
|
+
__classPrivateFieldSet(this, _UploadPacer_now, now, "f");
|
|
51
|
+
}
|
|
52
|
+
async wait(cost, signal) {
|
|
53
|
+
const waitMs = __classPrivateFieldGet(this, _UploadPacer_instances, "m", _UploadPacer_reserve).call(this, __classPrivateFieldGet(this, _UploadPacer_now, "f").call(this), cost);
|
|
54
|
+
if (waitMs <= 0) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
await delay(waitMs, undefined, signal ? { signal } : undefined);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
_UploadPacer_requests = new WeakMap(), _UploadPacer_files = new WeakMap(), _UploadPacer_bytes = new WeakMap(), _UploadPacer_now = new WeakMap(), _UploadPacer_instances = new WeakSet(), _UploadPacer_reserve = function _UploadPacer_reserve(now, cost) {
|
|
61
|
+
let start = now;
|
|
62
|
+
for (const budget of [__classPrivateFieldGet(this, _UploadPacer_requests, "f"), __classPrivateFieldGet(this, _UploadPacer_files, "f"), __classPrivateFieldGet(this, _UploadPacer_bytes, "f")]) {
|
|
63
|
+
if (isBudgetEnabled(budget) && budget.next > start) {
|
|
64
|
+
start = budget.next;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
__classPrivateFieldGet(this, _UploadPacer_instances, "m", _UploadPacer_reserveAt).call(this, __classPrivateFieldGet(this, _UploadPacer_requests, "f"), start, cost.requests ?? 0);
|
|
68
|
+
__classPrivateFieldGet(this, _UploadPacer_instances, "m", _UploadPacer_reserveAt).call(this, __classPrivateFieldGet(this, _UploadPacer_files, "f"), start, cost.files ?? 0);
|
|
69
|
+
__classPrivateFieldGet(this, _UploadPacer_instances, "m", _UploadPacer_reserveAt).call(this, __classPrivateFieldGet(this, _UploadPacer_bytes, "f"), start, cost.bytes ?? 0);
|
|
70
|
+
return start > now ? start - now : 0;
|
|
71
|
+
}, _UploadPacer_reserveAt = function _UploadPacer_reserveAt(budget, start, cost) {
|
|
72
|
+
if (!isBudgetEnabled(budget) || cost <= 0) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
budget.next = start + scaledWindowCost(budget.windowMs, cost, budget.limit);
|
|
76
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type RetryOptions } from "./errors.js";
|
|
2
|
+
export type UploadRunResult<T> = {
|
|
3
|
+
deferred: false;
|
|
4
|
+
value: T;
|
|
5
|
+
} | {
|
|
6
|
+
deferred: true;
|
|
7
|
+
};
|
|
8
|
+
export declare class UploadQueue {
|
|
9
|
+
#private;
|
|
10
|
+
get pendingNames(): string[];
|
|
11
|
+
run<T>(name: string, task: () => Promise<T>, options?: RetryOptions & {
|
|
12
|
+
onDeferred?: (error?: unknown) => void;
|
|
13
|
+
}): Promise<UploadRunResult<T>>;
|
|
14
|
+
flush(retryOptions?: RetryOptions): Promise<string[]>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
+
};
|
|
6
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
+
};
|
|
12
|
+
var _UploadQueue_pending, _UploadQueue_suspended;
|
|
13
|
+
import { ErrorKind, classifyError, shouldRetryUpload, withUploadRetry } from "./errors.js";
|
|
14
|
+
const isSuspendingError = (error) => classifyError(error) === ErrorKind.ResourceRecoverable;
|
|
15
|
+
export class UploadQueue {
|
|
16
|
+
constructor() {
|
|
17
|
+
_UploadQueue_pending.set(this, new Map());
|
|
18
|
+
_UploadQueue_suspended.set(this, false);
|
|
19
|
+
}
|
|
20
|
+
get pendingNames() {
|
|
21
|
+
return [...__classPrivateFieldGet(this, _UploadQueue_pending, "f").keys()];
|
|
22
|
+
}
|
|
23
|
+
async run(name, task, options = {}) {
|
|
24
|
+
const { onDeferred, ...retryOptions } = options;
|
|
25
|
+
if (__classPrivateFieldGet(this, _UploadQueue_suspended, "f")) {
|
|
26
|
+
__classPrivateFieldGet(this, _UploadQueue_pending, "f").set(name, task);
|
|
27
|
+
onDeferred?.();
|
|
28
|
+
return { deferred: true };
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const value = await withUploadRetry(task, retryOptions);
|
|
32
|
+
__classPrivateFieldGet(this, _UploadQueue_pending, "f").delete(name);
|
|
33
|
+
return { deferred: false, value };
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (!shouldRetryUpload(error)) {
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
__classPrivateFieldGet(this, _UploadQueue_pending, "f").set(name, task);
|
|
40
|
+
__classPrivateFieldSet(this, _UploadQueue_suspended, isSuspendingError(error), "f");
|
|
41
|
+
onDeferred?.(error);
|
|
42
|
+
return { deferred: true };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async flush(retryOptions = {}) {
|
|
46
|
+
const remaining = new Map(__classPrivateFieldGet(this, _UploadQueue_pending, "f"));
|
|
47
|
+
for (const [name, task] of __classPrivateFieldGet(this, _UploadQueue_pending, "f")) {
|
|
48
|
+
try {
|
|
49
|
+
await withUploadRetry(task, retryOptions);
|
|
50
|
+
remaining.delete(name);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
__classPrivateFieldSet(this, _UploadQueue_pending, remaining, "f");
|
|
57
|
+
__classPrivateFieldSet(this, _UploadQueue_suspended, remaining.size > 0, "f");
|
|
58
|
+
return [...remaining.keys()];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
_UploadQueue_pending = new WeakMap(), _UploadQueue_suspended = new WeakMap();
|