@code-pushup/core 0.8.24 → 0.9.0

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/index.js CHANGED
@@ -461,7 +461,8 @@ var uploadConfigSchema = z10.object({
461
461
  description: "API key with write access to portal (use `process.env` for security)"
462
462
  }),
463
463
  organization: slugSchema("Organization slug from Code PushUp portal"),
464
- project: slugSchema("Project slug from Code PushUp portal")
464
+ project: slugSchema("Project slug from Code PushUp portal"),
465
+ timeout: z10.number({ description: "Request timeout in minutes (default is 5)" }).positive().int().optional()
465
466
  });
466
467
 
467
468
  // packages/models/src/lib/core-config.ts
@@ -565,15 +566,15 @@ function slugify(text) {
565
566
  return text.trim().toLowerCase().replace(/\s+|\//g, "-").replace(/[^a-z\d-]/g, "");
566
567
  }
567
568
  function formatBytes(bytes, decimals = 2) {
568
- bytes = Math.max(bytes, 0);
569
- if (!bytes) {
569
+ const positiveBytes = Math.max(bytes, 0);
570
+ if (positiveBytes === 0) {
570
571
  return "0 B";
571
572
  }
572
573
  const k = 1024;
573
574
  const dm = decimals < 0 ? 0 : decimals;
574
575
  const sizes = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
575
- const i = Math.floor(Math.log(bytes) / Math.log(k));
576
- return `${Number.parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
576
+ const i = Math.floor(Math.log(positiveBytes) / Math.log(k));
577
+ return `${Number.parseFloat((positiveBytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
577
578
  }
578
579
  function formatDuration(duration) {
579
580
  if (duration < 1e3) {
@@ -758,8 +759,7 @@ function getSeverityIcon(severity) {
758
759
  return "\u2139\uFE0F";
759
760
  }
760
761
  function calcDuration(start, stop) {
761
- stop ??= performance.now();
762
- return Math.floor(stop - start);
762
+ return Math.floor((stop ?? performance.now()) - start);
763
763
  }
764
764
  function countCategoryAudits(refs, plugins) {
765
765
  const groupLookup = plugins.reduce(
@@ -785,15 +785,15 @@ function countCategoryAudits(refs, plugins) {
785
785
  }, 0);
786
786
  }
787
787
  function getAuditByRef({ slug, weight, plugin }, plugins) {
788
- const auditPlugin = plugins.find(({ slug: slug2 }) => slug2 === plugin);
788
+ const auditPlugin = plugins.find((p) => p.slug === plugin);
789
789
  if (!auditPlugin) {
790
790
  throwIsNotPresentError(`Plugin ${plugin}`, "report");
791
791
  }
792
- const audit = auditPlugin?.audits.find(
792
+ const audit = auditPlugin.audits.find(
793
793
  ({ slug: auditSlug }) => auditSlug === slug
794
794
  );
795
795
  if (!audit) {
796
- throwIsNotPresentError(`Audit ${slug}`, auditPlugin?.slug);
796
+ throwIsNotPresentError(`Audit ${slug}`, auditPlugin.slug);
797
797
  }
798
798
  return {
799
799
  ...audit,
@@ -806,24 +806,21 @@ function getGroupWithAudits(refSlug, refPlugin, plugins) {
806
806
  if (!plugin) {
807
807
  throwIsNotPresentError(`Plugin ${refPlugin}`, "report");
808
808
  }
809
- const groupWithAudits = plugin?.groups?.find(({ slug }) => slug === refSlug);
809
+ const groupWithAudits = plugin.groups?.find(({ slug }) => slug === refSlug);
810
810
  if (!groupWithAudits) {
811
- throwIsNotPresentError(`Group ${refSlug}`, plugin?.slug);
811
+ throwIsNotPresentError(`Group ${refSlug}`, plugin.slug);
812
812
  }
813
813
  const groupAudits = groupWithAudits.refs.reduce(
814
814
  (acc, ref) => {
815
815
  const audit = getAuditByRef(
816
- { ...ref, plugin: refPlugin },
816
+ { ...ref, plugin: refPlugin, type: "audit" },
817
817
  plugins
818
818
  );
819
- if (audit) {
820
- return [...acc, audit];
821
- }
822
- return [...acc];
819
+ return [...acc, audit];
823
820
  },
824
821
  []
825
822
  );
826
- const audits = groupAudits.sort(compareCategoryAudits);
823
+ const audits = [...groupAudits].sort(compareCategoryAudits);
827
824
  return {
828
825
  ...groupWithAudits,
829
826
  audits
@@ -866,7 +863,8 @@ async function loadReport(options) {
866
863
  const content = await readJsonFile(filePath);
867
864
  return reportSchema.parse(content);
868
865
  }
869
- return readTextFile(filePath);
866
+ const text = await readTextFile(filePath);
867
+ return text;
870
868
  }
871
869
  function throwIsNotPresentError(itemName, presentPlace) {
872
870
  throw new Error(`${itemName} is not present in ${presentPlace}`);
@@ -921,11 +919,11 @@ function executeProcess(cfg) {
921
919
  let stdout = "";
922
920
  let stderr = "";
923
921
  process2.stdout.on("data", (data) => {
924
- stdout += data.toString();
925
- onStdout?.(data);
922
+ stdout += String(data);
923
+ onStdout?.(String(data));
926
924
  });
927
925
  process2.stderr.on("data", (data) => {
928
- stderr += data.toString();
926
+ stderr += String(data);
929
927
  });
930
928
  process2.on("error", (err) => {
931
929
  stderr += err.toString();
@@ -945,28 +943,20 @@ function executeProcess(cfg) {
945
943
  }
946
944
 
947
945
  // packages/utils/src/lib/git.ts
948
- import simpleGit from "simple-git";
946
+ import { simpleGit } from "simple-git";
949
947
  var git = simpleGit();
950
948
  async function getLatestCommit() {
951
949
  const log = await git.log({
952
950
  maxCount: 1,
953
951
  format: { hash: "%H", message: "%s", author: "%an", date: "%ad" }
954
952
  });
955
- return log?.latest;
953
+ return log.latest;
956
954
  }
957
955
 
958
956
  // packages/utils/src/lib/group-by-status.ts
959
957
  function groupByStatus(results) {
960
958
  return results.reduce(
961
- (acc, result) => {
962
- if (result.status === "fulfilled") {
963
- return { ...acc, fulfilled: [...acc.fulfilled, result] };
964
- }
965
- if (result.status === "rejected") {
966
- return { ...acc, rejected: [...acc.rejected, result] };
967
- }
968
- return acc;
969
- },
959
+ (acc, result) => result.status === "fulfilled" ? { ...acc, fulfilled: [...acc.fulfilled, result] } : { ...acc, rejected: [...acc.rejected, result] },
970
960
  { fulfilled: [], rejected: [] }
971
961
  );
972
962
  }
@@ -1081,10 +1071,10 @@ function tableMd(data, align) {
1081
1071
  if (data.length === 0) {
1082
1072
  throw new Error("Data can't be empty");
1083
1073
  }
1084
- align ??= data[0]?.map(() => "c");
1074
+ const alignmentSetting = align ?? data[0]?.map(() => "c");
1085
1075
  const tableContent = data.map((arr) => `|${arr.join("|")}|`);
1086
- const secondRow = `|${align?.map((s) => alignString.get(s)).join("|")}|`;
1087
- return tableContent.shift() + NEW_LINE + secondRow + NEW_LINE + tableContent.join(NEW_LINE);
1076
+ const alignmentRow = `|${alignmentSetting?.map((s) => alignString.get(s)).join("|")}|`;
1077
+ return tableContent[0] + NEW_LINE + alignmentRow + NEW_LINE + tableContent.slice(1).join(NEW_LINE);
1088
1078
  }
1089
1079
  function tableHtml(data) {
1090
1080
  if (data.length === 0) {
@@ -1137,15 +1127,15 @@ function reportToCategoriesSection(report) {
1137
1127
  category.score
1138
1128
  )} Score: ${style(formatReportScore(category.score))}`;
1139
1129
  const categoryDocs = getDocsAndDescription(category);
1140
- const categoryMDItems = category.refs.reduce((acc2, ref) => {
1130
+ const categoryMDItems = category.refs.reduce((refAcc, ref) => {
1141
1131
  if (ref.type === "group") {
1142
1132
  const group = getGroupWithAudits(ref.slug, ref.plugin, plugins);
1143
1133
  const mdGroupItem = groupItemToCategorySection(group, plugins);
1144
- return acc2 + mdGroupItem + NEW_LINE;
1134
+ return refAcc + mdGroupItem + NEW_LINE;
1145
1135
  } else {
1146
1136
  const audit = getAuditByRef(ref, plugins);
1147
1137
  const mdAuditItem = auditItemToCategorySection(audit, plugins);
1148
- return acc2 + mdAuditItem + NEW_LINE;
1138
+ return refAcc + mdAuditItem + NEW_LINE;
1149
1139
  }
1150
1140
  }, "");
1151
1141
  return acc + NEW_LINE + categoryTitle + NEW_LINE + NEW_LINE + categoryDocs + categoryScore + NEW_LINE + categoryMDItems;
@@ -1156,7 +1146,7 @@ function auditItemToCategorySection(audit, plugins) {
1156
1146
  const pluginTitle = getPluginNameFromSlug(audit.plugin, plugins);
1157
1147
  const auditTitle = link(
1158
1148
  `#${slugify(audit.title)}-${slugify(pluginTitle)}`,
1159
- audit?.title
1149
+ audit.title
1160
1150
  );
1161
1151
  return li(
1162
1152
  `${getSquaredScoreMarker(
@@ -1173,69 +1163,57 @@ function groupItemToCategorySection(group, plugins) {
1173
1163
  const groupAudits = group.audits.reduce((acc, audit) => {
1174
1164
  const auditTitle = link(
1175
1165
  `#${slugify(audit.title)}-${slugify(pluginTitle)}`,
1176
- audit?.title
1166
+ audit.title
1177
1167
  );
1178
- acc += ` ${li(
1168
+ return `${acc} ${li(
1179
1169
  `${getSquaredScoreMarker(audit.score)} ${auditTitle} - ${getAuditResult(
1180
1170
  audit
1181
1171
  )}`
1182
- )}`;
1183
- acc += NEW_LINE;
1184
- return acc;
1172
+ )}${NEW_LINE}`;
1185
1173
  }, "");
1186
1174
  return groupTitle + NEW_LINE + groupAudits;
1187
1175
  }
1188
1176
  function reportToAuditsSection(report) {
1189
- const auditsSection = report.plugins.reduce((acc, plugin) => {
1190
- const auditsData = plugin.audits.reduce((acc2, audit) => {
1177
+ const auditsSection = report.plugins.reduce((pluginAcc, plugin) => {
1178
+ const auditsData = plugin.audits.reduce((auditAcc, audit) => {
1191
1179
  const auditTitle = `${audit.title} (${getPluginNameFromSlug(
1192
1180
  audit.plugin,
1193
1181
  report.plugins
1194
1182
  )})`;
1195
- const detailsTitle = `${getSquaredScoreMarker(
1196
- audit.score
1197
- )} ${getAuditResult(audit, true)} (score: ${formatReportScore(
1198
- audit.score
1199
- )})`;
1200
- const docsItem = getDocsAndDescription(audit);
1201
- acc2 += h3(auditTitle);
1202
- acc2 += NEW_LINE;
1203
- acc2 += NEW_LINE;
1204
- if (!audit.details?.issues?.length) {
1205
- acc2 += detailsTitle;
1206
- acc2 += NEW_LINE;
1207
- acc2 += NEW_LINE;
1208
- acc2 += docsItem;
1209
- return acc2;
1210
- }
1211
- const detailsTableData = [
1212
- detailsTableHeaders,
1213
- ...audit.details.issues.map((issue) => {
1214
- const severity = `${getSeverityIcon(issue.severity)} <i>${issue.severity}</i>`;
1215
- const message = issue.message;
1216
- if (!issue.source) {
1217
- return [severity, message, "", ""];
1218
- }
1219
- const file = `<code>${issue.source?.file}</code>`;
1220
- if (!issue.source.position) {
1221
- return [severity, message, file, ""];
1222
- }
1223
- const { startLine, endLine } = issue.source.position;
1224
- const line = `${startLine || ""}${endLine && startLine !== endLine ? `-${endLine}` : ""}`;
1225
- return [severity, message, file, line];
1226
- })
1227
- ];
1228
- const detailsTable = `<h4>Issues</h4>${tableHtml(detailsTableData)}`;
1229
- acc2 += details(detailsTitle, detailsTable);
1230
- acc2 += NEW_LINE;
1231
- acc2 += NEW_LINE;
1232
- acc2 += docsItem;
1233
- return acc2;
1183
+ return auditAcc + h3(auditTitle) + NEW_LINE + NEW_LINE + reportToDetailsSection(audit) + NEW_LINE + NEW_LINE + getDocsAndDescription(audit);
1234
1184
  }, "");
1235
- return acc + auditsData;
1185
+ return pluginAcc + auditsData;
1236
1186
  }, "");
1237
1187
  return h2("\u{1F6E1}\uFE0F Audits") + NEW_LINE + NEW_LINE + auditsSection;
1238
1188
  }
1189
+ function reportToDetailsSection(audit) {
1190
+ const detailsTitle = `${getSquaredScoreMarker(audit.score)} ${getAuditResult(
1191
+ audit,
1192
+ true
1193
+ )} (score: ${formatReportScore(audit.score)})`;
1194
+ if (!audit.details?.issues.length) {
1195
+ return detailsTitle;
1196
+ }
1197
+ const detailsTableData = [
1198
+ detailsTableHeaders,
1199
+ ...audit.details.issues.map((issue) => {
1200
+ const severity = `${getSeverityIcon(issue.severity)} <i>${issue.severity}</i>`;
1201
+ const message = issue.message;
1202
+ if (!issue.source) {
1203
+ return [severity, message, "", ""];
1204
+ }
1205
+ const file = `<code>${issue.source.file}</code>`;
1206
+ if (!issue.source.position) {
1207
+ return [severity, message, file, ""];
1208
+ }
1209
+ const { startLine, endLine } = issue.source.position;
1210
+ const line = `${startLine || ""}${endLine && startLine !== endLine ? `-${endLine}` : ""}`;
1211
+ return [severity, message, file, line];
1212
+ })
1213
+ ];
1214
+ const detailsTable = `<h4>Issues</h4>${tableHtml(detailsTableData)}`;
1215
+ return details(detailsTitle, detailsTable);
1216
+ }
1239
1217
  function reportToAboutSection(report, commitData) {
1240
1218
  const date = (/* @__PURE__ */ new Date()).toString();
1241
1219
  const { duration, version: version2, plugins, categories } = report;
@@ -1253,11 +1231,11 @@ function reportToAboutSection(report, commitData) {
1253
1231
  ];
1254
1232
  const pluginMetaTable = [
1255
1233
  pluginMetaTableHeaders,
1256
- ...plugins.map(({ title, version: version3, duration: duration2, audits }) => [
1257
- title,
1258
- audits.length.toString(),
1259
- style(version3 || "", ["c"]),
1260
- formatDuration(duration2)
1234
+ ...plugins.map((plugin) => [
1235
+ plugin.title,
1236
+ plugin.audits.length.toString(),
1237
+ style(plugin.version || "", ["c"]),
1238
+ formatDuration(plugin.duration)
1261
1239
  ])
1262
1240
  ];
1263
1241
  return (
@@ -1292,7 +1270,7 @@ function getAuditResult(audit, isHtml = false) {
1292
1270
  // packages/utils/src/lib/reports/generate-stdout-summary.ts
1293
1271
  import cliui from "@isaacs/cliui";
1294
1272
  import chalk3 from "chalk";
1295
- import Table from "cli-table3";
1273
+ import CliTable3 from "cli-table3";
1296
1274
  function addLine(line = "") {
1297
1275
  return line + NEW_LINE;
1298
1276
  }
@@ -1308,20 +1286,20 @@ function reportToDetailSection(report) {
1308
1286
  return plugins.reduce((acc, plugin) => {
1309
1287
  const { title, audits } = plugin;
1310
1288
  const ui = cliui({ width: TERMINAL_WIDTH });
1311
- audits.forEach(({ score, title: title2, displayValue, value }) => {
1289
+ audits.forEach((audit) => {
1312
1290
  ui.div(
1313
1291
  {
1314
- text: withColor({ score, text: "\u25CF" }),
1292
+ text: withColor({ score: audit.score, text: "\u25CF" }),
1315
1293
  width: 2,
1316
1294
  padding: [0, 1, 0, 0]
1317
1295
  },
1318
1296
  {
1319
- text: title2,
1297
+ text: audit.title,
1320
1298
  // eslint-disable-next-line no-magic-numbers
1321
1299
  padding: [0, 3, 0, 0]
1322
1300
  },
1323
1301
  {
1324
- text: chalk3.cyanBright(displayValue || `${value}`),
1302
+ text: chalk3.cyanBright(audit.displayValue || `${audit.value}`),
1325
1303
  width: 10,
1326
1304
  padding: [0, 0, 0, 0]
1327
1305
  }
@@ -1334,7 +1312,7 @@ function reportToOverviewSection2({
1334
1312
  categories,
1335
1313
  plugins
1336
1314
  }) {
1337
- const table = new Table({
1315
+ const table = new CliTable3({
1338
1316
  head: reportRawOverviewTableHeaders,
1339
1317
  colAligns: ["left", "right", "right"],
1340
1318
  style: {
@@ -1367,16 +1345,7 @@ function toArray(val) {
1367
1345
  return Array.isArray(val) ? val : [val];
1368
1346
  }
1369
1347
  function deepClone(obj) {
1370
- if (obj == null || typeof obj !== "object") {
1371
- return obj;
1372
- }
1373
- const cloned = Array.isArray(obj) ? [] : {};
1374
- for (const key in obj) {
1375
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
1376
- cloned[key] = deepClone(obj[key]);
1377
- }
1378
- }
1379
- return cloned;
1348
+ return obj == null || typeof obj !== "object" ? obj : structuredClone(obj);
1380
1349
  }
1381
1350
 
1382
1351
  // packages/utils/src/lib/reports/scoring.ts
@@ -1399,15 +1368,12 @@ function calculateScore(refs, scoreFn) {
1399
1368
  return numerator / denominator;
1400
1369
  }
1401
1370
  function scoreReport(report) {
1402
- const scoredReport = deepClone(report);
1403
1371
  const allScoredAuditsAndGroups = /* @__PURE__ */ new Map();
1404
- scoredReport.plugins?.forEach((plugin) => {
1405
- const { slug, audits } = plugin;
1406
- const groups = plugin.groups || [];
1407
- audits.forEach((audit) => {
1408
- const key = `${slug}-${audit.slug}-audit`;
1409
- audit.plugin = slug;
1410
- allScoredAuditsAndGroups.set(key, audit);
1372
+ const scoredPlugins = report.plugins.map((plugin) => {
1373
+ const { slug, audits, groups } = plugin;
1374
+ const updatedAudits = audits.map((audit) => ({ ...audit, plugin: slug }));
1375
+ updatedAudits.forEach((audit) => {
1376
+ allScoredAuditsAndGroups.set(`${slug}-${audit.slug}-audit`, audit);
1411
1377
  });
1412
1378
  function groupScoreFn(ref) {
1413
1379
  const score = allScoredAuditsAndGroups.get(
@@ -1420,13 +1386,15 @@ function scoreReport(report) {
1420
1386
  }
1421
1387
  return score;
1422
1388
  }
1423
- groups.forEach((group) => {
1424
- const key = `${slug}-${group.slug}-group`;
1425
- group.score = calculateScore(group.refs, groupScoreFn);
1426
- group.plugin = slug;
1427
- allScoredAuditsAndGroups.set(key, group);
1389
+ const scoredGroups = groups?.map((group) => ({
1390
+ ...group,
1391
+ score: calculateScore(group.refs, groupScoreFn),
1392
+ plugin: slug
1393
+ })) ?? [];
1394
+ scoredGroups.forEach((group) => {
1395
+ allScoredAuditsAndGroups.set(`${slug}-${group.slug}-group`, group);
1428
1396
  });
1429
- plugin.groups = groups;
1397
+ return { ...plugin, audits: updatedAudits, groups: scoredGroups };
1430
1398
  });
1431
1399
  function catScoreFn(ref) {
1432
1400
  const key = `${ref.plugin}-${ref.slug}-${ref.type}`;
@@ -1438,13 +1406,15 @@ function scoreReport(report) {
1438
1406
  }
1439
1407
  return item.score;
1440
1408
  }
1441
- const scoredCategoriesMap = /* @__PURE__ */ new Map();
1442
- for (const category of scoredReport.categories) {
1443
- category.score = calculateScore(category.refs, catScoreFn);
1444
- scoredCategoriesMap.set(category.slug, category);
1445
- }
1446
- scoredReport.categories = [...scoredCategoriesMap.values()];
1447
- return scoredReport;
1409
+ const scoredCategories = report.categories.map((category) => ({
1410
+ ...category,
1411
+ score: calculateScore(category.refs, catScoreFn)
1412
+ }));
1413
+ return {
1414
+ ...deepClone(report),
1415
+ plugins: scoredPlugins,
1416
+ categories: scoredCategories
1417
+ };
1448
1418
  }
1449
1419
 
1450
1420
  // packages/utils/src/lib/reports/sorting.ts
@@ -1467,7 +1437,7 @@ function sortReport(report) {
1467
1437
  );
1468
1438
  const sortedAuditsAndGroups = [
1469
1439
  ...groups,
1470
- ...audits.sort(compareCategoryAudits)
1440
+ ...[...audits].sort(compareCategoryAudits)
1471
1441
  ];
1472
1442
  const sortedRefs = [...category.refs].sort((a, b) => {
1473
1443
  const aIndex = sortedAuditsAndGroups.findIndex(
@@ -1480,7 +1450,14 @@ function sortReport(report) {
1480
1450
  });
1481
1451
  return { ...category, refs: sortedRefs };
1482
1452
  });
1483
- const sortedPlugins = plugins.map((plugin) => ({
1453
+ return {
1454
+ ...report,
1455
+ categories: sortedCategories,
1456
+ plugins: sortPlugins(plugins)
1457
+ };
1458
+ }
1459
+ function sortPlugins(plugins) {
1460
+ return plugins.map((plugin) => ({
1484
1461
  ...plugin,
1485
1462
  audits: [...plugin.audits].sort(compareAudits).map(
1486
1463
  (audit) => audit.details?.issues ? {
@@ -1492,11 +1469,6 @@ function sortReport(report) {
1492
1469
  } : audit
1493
1470
  )
1494
1471
  }));
1495
- return {
1496
- ...report,
1497
- categories: sortedCategories,
1498
- plugins: sortedPlugins
1499
- };
1500
1472
  }
1501
1473
 
1502
1474
  // packages/utils/src/lib/verbose-utils.ts
@@ -1696,7 +1668,7 @@ function auditOutputsCorrelateWithPluginOutput(auditOutputs, pluginConfigAudits)
1696
1668
 
1697
1669
  // packages/core/package.json
1698
1670
  var name = "@code-pushup/core";
1699
- var version = "0.8.24";
1671
+ var version = "0.9.0";
1700
1672
 
1701
1673
  // packages/core/src/lib/implementation/collect.ts
1702
1674
  async function collect(options) {
@@ -1817,7 +1789,7 @@ async function upload(options, uploadFn = uploadToPortal) {
1817
1789
  if (!options.upload) {
1818
1790
  throw new Error("upload config must be set");
1819
1791
  }
1820
- const { apiKey, server, organization, project } = options.upload;
1792
+ const { apiKey, server, organization, project, timeout } = options.upload;
1821
1793
  const report = await loadReport({
1822
1794
  ...persist,
1823
1795
  format: "json"
@@ -1832,7 +1804,7 @@ async function upload(options, uploadFn = uploadToPortal) {
1832
1804
  commit: commitData.hash,
1833
1805
  ...jsonReportToGql(report)
1834
1806
  };
1835
- return uploadFn({ apiKey, server, data });
1807
+ return uploadFn({ apiKey, server, data, timeout });
1836
1808
  }
1837
1809
 
1838
1810
  // packages/core/src/lib/collect-and-persist.ts
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@code-pushup/core",
3
- "version": "0.8.24",
3
+ "version": "0.9.0",
4
4
  "dependencies": {
5
5
  "@code-pushup/models": "*",
6
6
  "@code-pushup/utils": "*",
7
- "@code-pushup/portal-client": "^0.3.0",
7
+ "@code-pushup/portal-client": "^0.4.0",
8
8
  "chalk": "^5.3.0"
9
9
  },
10
10
  "type": "module",
@@ -2,7 +2,7 @@ import { uploadToPortal } from '@code-pushup/portal-client';
2
2
  import { PersistConfig, UploadConfig } from '@code-pushup/models';
3
3
  import { GlobalOptions } from './types';
4
4
  export type UploadOptions = {
5
- upload: Required<UploadConfig>;
5
+ upload: UploadConfig;
6
6
  } & {
7
7
  persist: Required<PersistConfig>;
8
8
  } & GlobalOptions;