@allurereport/plugin-testops 3.15.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 +182 -109
- 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/logger.d.ts +3 -1
- package/dist/logger.js +6 -2
- package/dist/model.d.ts +26 -1
- package/dist/plugin.js +175 -160
- 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,39 +9,50 @@ 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_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, []);
|
|
40
43
|
_TestOpsPlugin_uploadedTestResultsIds.set(this, new Set());
|
|
44
|
+
_TestOpsPlugin_uploadedGlobalAttachmentIds.set(this, new Set());
|
|
45
|
+
_TestOpsPlugin_uploadedGlobalErrors.set(this, new Set());
|
|
41
46
|
_TestOpsPlugin_autocloseLaunch.set(this, false);
|
|
42
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);
|
|
43
51
|
_TestOpsPlugin_gitFlow.set(this, void 0);
|
|
44
52
|
_TestOpsPlugin_enabledByConfig.set(this, false);
|
|
53
|
+
_TestOpsPlugin_uploadQueue.set(this, new UploadQueue());
|
|
54
|
+
applyAllureCiEnv();
|
|
55
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_ci, applyCiOverrides(detect()), "f");
|
|
45
56
|
__classPrivateFieldSet(this, _TestOpsPlugin_enabledByConfig, context.enabled === true, "f");
|
|
46
57
|
if (context.enabled === false) {
|
|
47
58
|
return;
|
|
@@ -50,17 +61,20 @@ export class TestOpsPlugin {
|
|
|
50
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.`);
|
|
51
62
|
return;
|
|
52
63
|
}
|
|
53
|
-
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);
|
|
54
65
|
if ([accessToken, endpoint, projectId].every(Boolean)) {
|
|
55
66
|
__classPrivateFieldSet(this, _TestOpsPlugin_client, new TestOpsClient({
|
|
56
67
|
baseUrl: endpoint,
|
|
57
68
|
accessToken,
|
|
58
69
|
projectId,
|
|
70
|
+
uploadRateLimit,
|
|
59
71
|
}), "f");
|
|
60
72
|
__classPrivateFieldSet(this, _TestOpsPlugin_launchName, launchName, "f");
|
|
61
73
|
__classPrivateFieldSet(this, _TestOpsPlugin_launchTags, launchTags, "f");
|
|
62
74
|
}
|
|
63
75
|
__classPrivateFieldSet(this, _TestOpsPlugin_autocloseLaunch, autocloseLaunch, "f");
|
|
76
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_reopenClosedLaunch, reopenClosedLaunch, "f");
|
|
77
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_launchId, launchId, "f");
|
|
64
78
|
const gitFlowOptions = resolveGitFlowOptions(options);
|
|
65
79
|
__classPrivateFieldSet(this, _TestOpsPlugin_gitFlow, new LaunchGitFlow({
|
|
66
80
|
ci: __classPrivateFieldGet(this, _TestOpsPlugin_ci, "f"),
|
|
@@ -85,7 +99,7 @@ export class TestOpsPlugin {
|
|
|
85
99
|
}
|
|
86
100
|
return ["true", "1"].includes(value);
|
|
87
101
|
};
|
|
88
|
-
return isEnabled(env.ALLURE_TESTOPS_ENABLED) || isEnabled(env.CI);
|
|
102
|
+
return isEnabled(env.ALLURE_TESTOPS_ENABLED) || isEnabled(env.CI) || resolveJobRunIdFromEnv() !== undefined;
|
|
89
103
|
}
|
|
90
104
|
get isManuallyEnabled() {
|
|
91
105
|
return __classPrivateFieldGet(this, _TestOpsPlugin_enabledByConfig, "f") || this.isOverridenByEnv;
|
|
@@ -106,6 +120,9 @@ export class TestOpsPlugin {
|
|
|
106
120
|
if (!this.enabled) {
|
|
107
121
|
return;
|
|
108
122
|
}
|
|
123
|
+
if (context.realTime) {
|
|
124
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").setLogLevel("info");
|
|
125
|
+
}
|
|
109
126
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("Starting upload…");
|
|
110
127
|
if (!(await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_startUpload).call(this))) {
|
|
111
128
|
return;
|
|
@@ -139,6 +156,15 @@ export class TestOpsPlugin {
|
|
|
139
156
|
const worstStatus = getWorstStatus(allTrs.map(({ status }) => status));
|
|
140
157
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("Finalizing upload…");
|
|
141
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
|
+
}
|
|
142
168
|
try {
|
|
143
169
|
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_stopUpload).call(this, worstStatus || "unknown");
|
|
144
170
|
}
|
|
@@ -162,6 +188,10 @@ export class TestOpsPlugin {
|
|
|
162
188
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info(`Upload finished. Allure TestOps Launch: ${__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").launchUrl}`);
|
|
163
189
|
return;
|
|
164
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
|
+
}
|
|
165
195
|
let launchIsReady = false;
|
|
166
196
|
for (let attempt = 0; attempt < LAUNCH_PROGRESS_ATTEMPTS_LIMIT; attempt += 1) {
|
|
167
197
|
launchIsReady = await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").checkLaunchProgress();
|
|
@@ -209,7 +239,45 @@ export class TestOpsPlugin {
|
|
|
209
239
|
return summary;
|
|
210
240
|
}
|
|
211
241
|
}
|
|
212
|
-
_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_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) {
|
|
213
281
|
const results = await store.qualityGateResults();
|
|
214
282
|
const uniqueResults = uniqBy(results.filter(({ success }) => !success), ({ rule, environment }) => `${rule}-${environment}`);
|
|
215
283
|
if (uniqueResults.length === 0) {
|
|
@@ -225,36 +293,29 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
225
293
|
let completed = false;
|
|
226
294
|
try {
|
|
227
295
|
progressLogger.log(true);
|
|
228
|
-
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) => {
|
|
229
297
|
if (!completed && percent >= 100) {
|
|
230
298
|
completed = true;
|
|
231
299
|
progressLogger.increment();
|
|
232
300
|
}
|
|
233
|
-
});
|
|
301
|
+
}), __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_retryHooks).call(this, "quality gate results"));
|
|
302
|
+
if (result.deferred) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
234
305
|
if (!completed) {
|
|
235
306
|
progressLogger.increment();
|
|
236
307
|
}
|
|
237
308
|
progressLogger.log(true);
|
|
238
309
|
}
|
|
239
310
|
catch (error) {
|
|
240
|
-
|
|
241
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload quality gate results: ${error.response.data.message}`);
|
|
242
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
243
|
-
}
|
|
244
|
-
else if (error instanceof Error) {
|
|
245
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload quality gate results: ${error.message}`);
|
|
246
|
-
}
|
|
247
|
-
else {
|
|
248
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error("Failed to upload quality gate results");
|
|
249
|
-
}
|
|
311
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_logUploadFailure).call(this, "quality gate results", error);
|
|
250
312
|
}
|
|
251
313
|
finally {
|
|
252
314
|
progressLogger.cancel?.();
|
|
253
315
|
}
|
|
254
|
-
}, _TestOpsPlugin_uploadGlobalErrors = async function _TestOpsPlugin_uploadGlobalErrors(
|
|
255
|
-
const results = await store.allGlobalErrors();
|
|
316
|
+
}, _TestOpsPlugin_uploadGlobalErrors = async function _TestOpsPlugin_uploadGlobalErrors(results) {
|
|
256
317
|
if (results.length === 0) {
|
|
257
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("No global errors to upload");
|
|
318
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("No new global errors to upload");
|
|
258
319
|
return;
|
|
259
320
|
}
|
|
260
321
|
const progressLogger = createProgressLogger({
|
|
@@ -266,36 +327,32 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
266
327
|
let completed = false;
|
|
267
328
|
try {
|
|
268
329
|
progressLogger.log(true);
|
|
269
|
-
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) => {
|
|
270
331
|
if (!completed && percent >= 100) {
|
|
271
332
|
completed = true;
|
|
272
333
|
progressLogger.increment();
|
|
273
334
|
}
|
|
274
|
-
});
|
|
335
|
+
}), __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_retryHooks).call(this, "global errors"));
|
|
336
|
+
if (result.deferred) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
275
339
|
if (!completed) {
|
|
276
340
|
progressLogger.increment();
|
|
277
341
|
}
|
|
342
|
+
results.forEach((error) => {
|
|
343
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_uploadedGlobalErrors, "f").add(error);
|
|
344
|
+
});
|
|
278
345
|
progressLogger.log(true);
|
|
279
346
|
}
|
|
280
347
|
catch (error) {
|
|
281
|
-
|
|
282
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload global errors: ${error.response.data.message}`);
|
|
283
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
284
|
-
}
|
|
285
|
-
else if (error instanceof Error) {
|
|
286
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload global errors: ${error.message}`);
|
|
287
|
-
}
|
|
288
|
-
else {
|
|
289
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error("Failed to upload global errors");
|
|
290
|
-
}
|
|
348
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_logUploadFailure).call(this, "global errors", error);
|
|
291
349
|
}
|
|
292
350
|
finally {
|
|
293
351
|
progressLogger.cancel?.();
|
|
294
352
|
}
|
|
295
|
-
}, _TestOpsPlugin_uploadGlobalAttachments = async function _TestOpsPlugin_uploadGlobalAttachments(store) {
|
|
296
|
-
const attachments = await store.allGlobalAttachments();
|
|
353
|
+
}, _TestOpsPlugin_uploadGlobalAttachments = async function _TestOpsPlugin_uploadGlobalAttachments(store, attachments) {
|
|
297
354
|
if (attachments.length === 0) {
|
|
298
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug("No global attachments to upload");
|
|
355
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug("No new global attachments to upload");
|
|
299
356
|
return;
|
|
300
357
|
}
|
|
301
358
|
const progressLogger = createProgressLogger({
|
|
@@ -307,7 +364,7 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
307
364
|
let completed = false;
|
|
308
365
|
try {
|
|
309
366
|
progressLogger.log(true);
|
|
310
|
-
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({
|
|
311
368
|
attachments,
|
|
312
369
|
attachmentsResolver: async (attachmentLink) => {
|
|
313
370
|
const content = await store.attachmentContentById(attachmentLink.id);
|
|
@@ -320,6 +377,7 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
320
377
|
originalFileName: filename,
|
|
321
378
|
contentType: attachmentLink.contentType ?? "application/octet-stream",
|
|
322
379
|
content: body,
|
|
380
|
+
contentLength: content?.getContentLength(),
|
|
323
381
|
};
|
|
324
382
|
},
|
|
325
383
|
onProgress: (percent) => {
|
|
@@ -328,77 +386,102 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
328
386
|
progressLogger.increment();
|
|
329
387
|
}
|
|
330
388
|
},
|
|
331
|
-
});
|
|
389
|
+
}), __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_retryHooks).call(this, "global attachments"));
|
|
390
|
+
if (result.deferred) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
332
393
|
if (!completed) {
|
|
333
394
|
progressLogger.increment();
|
|
334
395
|
}
|
|
396
|
+
attachments.forEach((attachment) => {
|
|
397
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_uploadedGlobalAttachmentIds, "f").add(attachment.id);
|
|
398
|
+
});
|
|
335
399
|
progressLogger.log(true);
|
|
336
400
|
}
|
|
337
401
|
catch (error) {
|
|
338
|
-
|
|
339
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload global attachments: ${error.response.data.message}`);
|
|
340
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
341
|
-
}
|
|
342
|
-
else if (error instanceof Error) {
|
|
343
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to upload global attachments: ${error.message}`);
|
|
344
|
-
}
|
|
345
|
-
else {
|
|
346
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error("Failed to upload global attachments");
|
|
347
|
-
}
|
|
402
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_logUploadFailure).call(this, "global attachments", error);
|
|
348
403
|
}
|
|
349
404
|
finally {
|
|
350
405
|
progressLogger.cancel?.();
|
|
351
406
|
}
|
|
352
|
-
}, _TestOpsPlugin_uploadTestResults = async function _TestOpsPlugin_uploadTestResults(store, trsToUpload, environments) {
|
|
407
|
+
}, _TestOpsPlugin_uploadTestResults = async function _TestOpsPlugin_uploadTestResults(store, trsToUpload, environments, options) {
|
|
353
408
|
const totalCount = trsToUpload.length;
|
|
354
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").
|
|
409
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose(`Preparing to upload ${bold(totalCount.toString())} ${totalCount > 1 ? "test results" : "test result"}`);
|
|
355
410
|
const progressLogger = createProgressLogger({
|
|
356
411
|
total: totalCount,
|
|
357
412
|
message: "Uploading test results",
|
|
358
413
|
unitLabel: totalCount === 1 ? "test result uploaded" : "test results uploaded",
|
|
359
414
|
prefix: "[TestOpsPlugin]",
|
|
415
|
+
silent: !!options?.silent,
|
|
360
416
|
});
|
|
361
|
-
const logProgress = progressLogger.log;
|
|
362
|
-
const incrementProgress = progressLogger.increment;
|
|
363
417
|
try {
|
|
364
|
-
|
|
365
|
-
const
|
|
418
|
+
progressLogger.log(true);
|
|
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({
|
|
366
421
|
attachmentsResolver: attachmentsResolverFactory(store),
|
|
367
422
|
fixturesResolver: fixturesResolverFactory(store),
|
|
368
423
|
environments,
|
|
369
|
-
trs: trsToUpload,
|
|
370
|
-
onProgress: () =>
|
|
371
|
-
|
|
372
|
-
|
|
424
|
+
trs: trsToUpload.filter((tr) => !__classPrivateFieldGet(this, _TestOpsPlugin_uploadedTestResultsIds, "f").has(tr.id)),
|
|
425
|
+
onProgress: () => progressLogger.increment(),
|
|
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;
|
|
435
|
+
progressLogger.log(true);
|
|
373
436
|
uploadedTrs.forEach((tr) => {
|
|
374
437
|
__classPrivateFieldGet(this, _TestOpsPlugin_uploadedTestResultsIds, "f").add(tr.id);
|
|
375
438
|
});
|
|
376
439
|
const uploadedCount = uploadedTrs.length;
|
|
377
440
|
if (uploadedCount === 0) {
|
|
378
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").
|
|
441
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("No test results were uploaded");
|
|
379
442
|
return;
|
|
380
443
|
}
|
|
381
444
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").info(`Uploaded ${uploadedCount} ${uploadedCount > 1 ? "test results" : "test result"}`);
|
|
382
445
|
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_logUploadFailure).call(this, "test results", error);
|
|
448
|
+
}
|
|
383
449
|
finally {
|
|
384
450
|
progressLogger.cancel?.();
|
|
385
451
|
}
|
|
386
452
|
}, _TestOpsPlugin_upload = async function _TestOpsPlugin_upload(store, options = {}) {
|
|
387
453
|
const { context, stage } = options;
|
|
388
454
|
const trsToUpload = await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_trsToUpload).call(this, store);
|
|
455
|
+
const shouldUploadGlobalArtifacts = stage === "done" || !context?.realTime;
|
|
456
|
+
let globalErrors = [];
|
|
457
|
+
let globalAttachments = [];
|
|
458
|
+
if (shouldUploadGlobalArtifacts) {
|
|
459
|
+
const [allGlobalErrors, allGlobalAttachments] = await Promise.all([
|
|
460
|
+
store.allGlobalErrors(),
|
|
461
|
+
store.allGlobalAttachments(),
|
|
462
|
+
]);
|
|
463
|
+
globalErrors = allGlobalErrors.filter((error) => !__classPrivateFieldGet(this, _TestOpsPlugin_uploadedGlobalErrors, "f").has(error));
|
|
464
|
+
globalAttachments = allGlobalAttachments.filter((attachment) => !__classPrivateFieldGet(this, _TestOpsPlugin_uploadedGlobalAttachmentIds, "f").has(attachment.id));
|
|
465
|
+
}
|
|
389
466
|
if (trsToUpload.length === 0) {
|
|
390
467
|
if (stage == "update") {
|
|
391
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").
|
|
468
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("No new test results to upload");
|
|
392
469
|
}
|
|
393
470
|
if (stage === "done") {
|
|
394
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").
|
|
471
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose("No test results to upload");
|
|
395
472
|
}
|
|
396
|
-
|
|
473
|
+
if (globalErrors.length === 0 && globalAttachments.length === 0) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (stage === "update" && trsToUpload.length > 0) {
|
|
478
|
+
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").verbose(`Found ${bold(trsToUpload.length.toString())} new test ${trsToUpload.length > 1 ? "results" : "result"}, uploading…`);
|
|
397
479
|
}
|
|
398
480
|
try {
|
|
399
|
-
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").createSession(env);
|
|
481
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").createSession(env, __classPrivateFieldGet(this, _TestOpsPlugin_reopenClosedLaunch, "f"));
|
|
400
482
|
}
|
|
401
483
|
catch (error) {
|
|
484
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_skippedUploadCycle, true, "f");
|
|
402
485
|
if (__classPrivateFieldGet(this, _TestOpsPlugin_client, "f").isTestOpsClientError(error)) {
|
|
403
486
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").error(`Failed to create TestOps session: ${error.response.data.message}`);
|
|
404
487
|
__classPrivateFieldGet(this, _TestOpsPlugin_logger, "f").debug(error.response?.data);
|
|
@@ -411,14 +494,23 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
411
494
|
}
|
|
412
495
|
return;
|
|
413
496
|
}
|
|
414
|
-
|
|
415
|
-
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m",
|
|
497
|
+
__classPrivateFieldSet(this, _TestOpsPlugin_skippedUploadCycle, false, "f");
|
|
498
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_uploadGlobalAttachments).call(this, store, globalAttachments);
|
|
499
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_uploadGlobalErrors).call(this, globalErrors);
|
|
500
|
+
if (trsToUpload.length === 0) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
416
503
|
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_uploadQualityGateResults).call(this, store);
|
|
417
504
|
const environments = await store.allEnvironmentIdentities();
|
|
418
505
|
const contextCategories = context?.categories ?? [];
|
|
419
|
-
const trsEnrichedWithCategories = await
|
|
420
|
-
await __classPrivateFieldGet(this,
|
|
421
|
-
|
|
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
|
+
}
|
|
511
|
+
await __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_uploadTestResults).call(this, store, trsEnrichedWithCategories, environments, {
|
|
512
|
+
silent: !!context?.realTime,
|
|
513
|
+
});
|
|
422
514
|
}, _TestOpsPlugin_trsToUpload = async function _TestOpsPlugin_trsToUpload(store) {
|
|
423
515
|
const filter = this.options.filter ?? stubTrue;
|
|
424
516
|
const filteredTrs = await store.allTestResults({
|
|
@@ -432,96 +524,19 @@ _TestOpsPlugin_logger = new WeakMap(), _TestOpsPlugin_ci = new WeakMap(), _TestO
|
|
|
432
524
|
includeRetries: false,
|
|
433
525
|
});
|
|
434
526
|
return filteredTrs;
|
|
435
|
-
}, _TestOpsPlugin_enrichWithCategories = async function _TestOpsPlugin_enrichWithCategories(store, trs, contextCategories) {
|
|
436
|
-
return Promise.all(trs.map(async (tr) => {
|
|
437
|
-
const environmentId = await store.environmentIdByTrId(tr.id);
|
|
438
|
-
const base = {
|
|
439
|
-
...tr,
|
|
440
|
-
...(environmentId ? { environment: environmentId } : {}),
|
|
441
|
-
steps: unwrapStepsAttachments(tr.steps),
|
|
442
|
-
};
|
|
443
|
-
const category = toUploadCategory(base, contextCategories ?? []);
|
|
444
|
-
if (category) {
|
|
445
|
-
base.category = category;
|
|
446
|
-
}
|
|
447
|
-
return base;
|
|
448
|
-
}));
|
|
449
|
-
}, _TestOpsPlugin_syncLaunchCategories = async function _TestOpsPlugin_syncLaunchCategories(trs, contextCategories) {
|
|
450
|
-
const categoryNamesByExternalId = __classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_collectCategoryNamesByExternalId).call(this, trs);
|
|
451
|
-
if (categoryNamesByExternalId.size === 0) {
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
const bulkItems = [];
|
|
455
|
-
const seenExternalIds = new Set();
|
|
456
|
-
for (const tr of trs) {
|
|
457
|
-
const cat = tr.category;
|
|
458
|
-
if (!cat?.externalId)
|
|
459
|
-
continue;
|
|
460
|
-
if (seenExternalIds.has(cat.externalId))
|
|
461
|
-
continue;
|
|
462
|
-
seenExternalIds.add(cat.externalId);
|
|
463
|
-
bulkItems.push({
|
|
464
|
-
externalId: cat.externalId,
|
|
465
|
-
name: categoryNamesByExternalId.get(cat.externalId) ?? categoryDisplayName(cat),
|
|
466
|
-
hide: cat.hide,
|
|
467
|
-
expand: cat.expand,
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
const rankByExternalId = new Map();
|
|
471
|
-
for (const c of contextCategories) {
|
|
472
|
-
if (!rankByExternalId.has(c.id)) {
|
|
473
|
-
rankByExternalId.set(c.id, c.index);
|
|
474
|
-
}
|
|
475
|
-
if (!rankByExternalId.has(c.name)) {
|
|
476
|
-
rankByExternalId.set(c.name, c.index);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
const ranked = bulkItems.map((item, i) => ({
|
|
480
|
-
item,
|
|
481
|
-
i,
|
|
482
|
-
rank: rankByExternalId.get(item.externalId),
|
|
483
|
-
}));
|
|
484
|
-
ranked.sort((a, b) => {
|
|
485
|
-
const ar = a.rank ?? Number.POSITIVE_INFINITY;
|
|
486
|
-
const br = b.rank ?? Number.POSITIVE_INFINITY;
|
|
487
|
-
if (ar !== br)
|
|
488
|
-
return ar - br;
|
|
489
|
-
return a.i - b.i;
|
|
490
|
-
});
|
|
491
|
-
const orderedBulkItems = ranked.map((r) => r.item);
|
|
492
|
-
const launchId = __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").launchId;
|
|
493
|
-
try {
|
|
494
|
-
const created = await __classPrivateFieldGet(this, _TestOpsPlugin_client, "f").createLaunchCategoriesBulk(launchId, orderedBulkItems);
|
|
495
|
-
const categoryIdByExternalId = new Map(created.map((r) => [r.externalId, r.id]));
|
|
496
|
-
__classPrivateFieldGet(this, _TestOpsPlugin_instances, "m", _TestOpsPlugin_assignCreatedCategoryIds).call(this, trs, categoryIdByExternalId);
|
|
497
|
-
}
|
|
498
|
-
catch {
|
|
499
|
-
}
|
|
500
|
-
}, _TestOpsPlugin_collectCategoryNamesByExternalId = function _TestOpsPlugin_collectCategoryNamesByExternalId(trs) {
|
|
501
|
-
const map = new Map();
|
|
502
|
-
for (const tr of trs) {
|
|
503
|
-
const cat = tr.category;
|
|
504
|
-
if (cat?.externalId) {
|
|
505
|
-
map.set(cat.externalId, categoryDisplayName(cat));
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
return map;
|
|
509
|
-
}, _TestOpsPlugin_assignCreatedCategoryIds = function _TestOpsPlugin_assignCreatedCategoryIds(trs, idByExternalId) {
|
|
510
|
-
for (const tr of trs) {
|
|
511
|
-
const cat = tr.category;
|
|
512
|
-
if (!cat?.externalId) {
|
|
513
|
-
continue;
|
|
514
|
-
}
|
|
515
|
-
const id = idByExternalId.get(cat.externalId);
|
|
516
|
-
if (typeof id === "number") {
|
|
517
|
-
tr.category = { ...cat, id };
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
527
|
}, _TestOpsPlugin_startUpload = async function _TestOpsPlugin_startUpload() {
|
|
521
|
-
const
|
|
528
|
+
const jobRunId = resolveJobRunIdFromEnv();
|
|
522
529
|
try {
|
|
523
|
-
|
|
524
|
-
|
|
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);
|
|
525
540
|
__classPrivateFieldSet(this, _TestOpsPlugin_launchStarted, true, "f");
|
|
526
541
|
}
|
|
527
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
|
+
};
|