@sentinelqa/playwright-reporter 0.1.53 → 0.1.54
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 +111 -146
- package/dist/localReport.js +360 -32
- package/dist/quickDiagnosis.d.ts +42 -2
- package/dist/quickDiagnosis.js +1326 -112
- package/dist/reporter.js +60 -25
- package/dist/runHistory.d.ts +3 -0
- package/dist/runHistory.js +11 -1
- package/dist/terminalSummary.js +44 -12
- package/package.json +2 -2
package/dist/reporter.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
const node_1 = require("@sentinelqa/uploader/node");
|
|
6
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
3
8
|
const env_1 = require("./env");
|
|
4
9
|
const quickDiagnosis_1 = require("./quickDiagnosis");
|
|
5
10
|
const terminalSummary_1 = require("./terminalSummary");
|
|
@@ -15,9 +20,10 @@ const styleCritical = (value) => colorize(value, "1;31");
|
|
|
15
20
|
const styleWarning = (value) => colorize(value, "1;33");
|
|
16
21
|
const styleAction = (value) => colorize(value, "1;36");
|
|
17
22
|
const stylePrimary = (value) => colorize(value, "1;97");
|
|
23
|
+
const styleImportant = (value) => colorize(value, "97");
|
|
18
24
|
const styleSecondary = (value) => colorize(value, "2");
|
|
19
25
|
const divider = () => "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━";
|
|
20
|
-
const LABEL_WIDTH =
|
|
26
|
+
const LABEL_WIDTH = 19;
|
|
21
27
|
const renderRow = (label, value, valueStyle, indent = "") => {
|
|
22
28
|
const paddedLabel = `${label}:`.padEnd(LABEL_WIDTH);
|
|
23
29
|
const styledValue = valueStyle ? valueStyle(value) : value;
|
|
@@ -31,8 +37,8 @@ const highlightCounts = (value) => value
|
|
|
31
37
|
.replace(/\b(\d+\s+passing\s+runs?)\b/gi, (_, match) => styleSecondary(match))
|
|
32
38
|
.replace(/\b(\d+\s+newly\s+failing)\b/gi, (_, match) => styleWarning(match))
|
|
33
39
|
.replace(/\b(\d+\s+tests?\s+still\s+failing)\b/gi, (_, match) => styleWarning(match));
|
|
34
|
-
const styleIssueTitle = (value) => value.replace(/\((\d+\s+tests?)\)/i, (_, match) => `(${styleWarning(match)})`);
|
|
35
|
-
const styleWhereValue = (value) => value.replace(/\b([A-Za-z0-9_.-]+\.[cm]?[jt]sx?:\d+(?::\d+)?)\b/g, (_, match) =>
|
|
40
|
+
const styleIssueTitle = (value) => stylePrimary(value.replace(/\((\d+\s+tests?)\)/i, (_, match) => `(${styleWarning(match)})`));
|
|
41
|
+
const styleWhereValue = (value) => value.replace(/\b([A-Za-z0-9_.-]+\.[cm]?[jt]sx?:\d+(?::\d+)?)\b/g, (_, match) => styleImportant(match));
|
|
36
42
|
const formatCliLine = (line) => {
|
|
37
43
|
if (!line.trim())
|
|
38
44
|
return line;
|
|
@@ -45,17 +51,19 @@ const formatCliLine = (line) => {
|
|
|
45
51
|
}
|
|
46
52
|
if (/^All tests passed$/.test(line))
|
|
47
53
|
return stylePrimary(line);
|
|
54
|
+
if (/^❌\s+\d+\s+failures?/.test(line))
|
|
55
|
+
return styleCritical(line);
|
|
48
56
|
if (/^\d+\s+tests?\s+failed$/.test(line))
|
|
49
57
|
return styleCritical(line);
|
|
50
58
|
if (/^Collapsed into \d+\s+real issue/.test(line))
|
|
51
59
|
return highlightCounts(line);
|
|
52
60
|
if (/^\d+\s+tests?\s+in\s+/.test(line))
|
|
53
61
|
return line;
|
|
54
|
-
if (/^Issue \d
|
|
62
|
+
if (/^Issue(?:(?: \d+)?):/.test(line)) {
|
|
55
63
|
const [head, rest] = line.split(": ", 2);
|
|
56
64
|
return `${stylePrimary(head)}: ${styleIssueTitle(rest || "")}`;
|
|
57
65
|
}
|
|
58
|
-
const rowMatch = line.match(/^(\s*)(What broke|Why|Cause|Where|What changed|Next|Expected|Received|Confidence|Impact|At risk|Why this matters|Recommendation|Last failure|Status|Artifacts ready|Failing step|
|
|
66
|
+
const rowMatch = line.match(/^(\s*)(What broke|Why|Cause|Where|What changed|Changes since last run|Next|Likely fix|Expected|Received|Confidence|Impact|At risk|Why this matters|Recommendation|Last failure|Status|Artifacts ready|Failing step|Selector|Target state|Report|Blocker):\s*(.*)$/);
|
|
59
67
|
if (!rowMatch)
|
|
60
68
|
return line;
|
|
61
69
|
const [, indent, label, rawValue] = rowMatch;
|
|
@@ -66,17 +74,19 @@ const formatCliLine = (line) => {
|
|
|
66
74
|
case "Why":
|
|
67
75
|
return renderRow(label, value, undefined, indent);
|
|
68
76
|
case "Cause":
|
|
69
|
-
return renderRow(label,
|
|
77
|
+
return renderRow(label, rawValue, undefined, indent);
|
|
70
78
|
case "Where":
|
|
71
79
|
return renderRow(label, rawValue, styleWhereValue, indent);
|
|
72
80
|
case "What changed":
|
|
81
|
+
case "Changes since last run":
|
|
73
82
|
return renderRow(label, rawValue, styleWarning, indent);
|
|
74
83
|
case "Next":
|
|
84
|
+
case "Likely fix":
|
|
75
85
|
return renderRow(label, rawValue, styleAction, indent);
|
|
76
86
|
case "Report":
|
|
77
87
|
return renderRow(label, rawValue, styleAction, indent);
|
|
78
88
|
case "Expected":
|
|
79
|
-
return renderRow(label, rawValue,
|
|
89
|
+
return renderRow(label, rawValue, styleImportant, indent);
|
|
80
90
|
case "Received":
|
|
81
91
|
return renderRow(label, rawValue, styleCritical, indent);
|
|
82
92
|
case "Confidence":
|
|
@@ -96,15 +106,13 @@ const formatCliLine = (line) => {
|
|
|
96
106
|
case "Artifacts ready":
|
|
97
107
|
return renderRow(label, rawValue, styleSecondary, indent);
|
|
98
108
|
case "Failing step":
|
|
99
|
-
return renderRow(label, rawValue,
|
|
100
|
-
case "Failing code":
|
|
101
|
-
return renderRow(label, rawValue, stylePrimary, indent);
|
|
109
|
+
return renderRow(label, rawValue, styleImportant, indent);
|
|
102
110
|
case "Selector":
|
|
103
|
-
return renderRow(label, rawValue,
|
|
111
|
+
return renderRow(label, rawValue, styleImportant, indent);
|
|
112
|
+
case "Blocker":
|
|
113
|
+
return renderRow(label, rawValue, styleCritical, indent);
|
|
104
114
|
case "Target state":
|
|
105
115
|
return renderRow(label, rawValue, styleWarning, indent);
|
|
106
|
-
case "Clears":
|
|
107
|
-
return renderRow(label, highlightCounts(rawValue), styleWarning, indent);
|
|
108
116
|
default:
|
|
109
117
|
return line;
|
|
110
118
|
}
|
|
@@ -177,12 +185,16 @@ class SentinelReporter {
|
|
|
177
185
|
!hasCiEnv &&
|
|
178
186
|
!localUploadEnabled;
|
|
179
187
|
const quickDiagnosis = (0, quickDiagnosis_1.buildQuickDiagnosis)(this.options.playwrightJsonPath);
|
|
188
|
+
const quickDiagnosisStructured = (0, quickDiagnosis_1.buildQuickDiagnosisStructured)(this.options.playwrightJsonPath);
|
|
189
|
+
const isSetupFailureDiagnosis = Boolean(quickDiagnosis?.lines.some((line) => /^Issue(?:(?: \d+)?): Playwright setup error/.test(line)));
|
|
180
190
|
const finalFailedCount = readFinalFailedCount(this.options.playwrightJsonPath);
|
|
181
191
|
const effectiveFailedCount = typeof finalFailedCount === "number" ? finalFailedCount : this.failedCount;
|
|
182
192
|
const passingSummary = effectiveFailedCount === 0
|
|
183
193
|
? (0, terminalSummary_1.buildPassingRunSummary)(this.options.playwrightJsonPath, { observedRunDurationMs: Date.now() - this.startedAt })
|
|
184
194
|
: null;
|
|
185
|
-
const failedRunHistory = effectiveFailedCount > 0
|
|
195
|
+
const failedRunHistory = effectiveFailedCount > 0 && !isSetupFailureDiagnosis
|
|
196
|
+
? (0, runHistory_1.buildFailedRunHistorySummary)(this.options.playwrightJsonPath)
|
|
197
|
+
: null;
|
|
186
198
|
if (effectiveFailedCount > 0) {
|
|
187
199
|
(0, terminalSummary_1.recordReporterHistorySnapshot)(this.options.playwrightJsonPath);
|
|
188
200
|
}
|
|
@@ -214,31 +226,53 @@ class SentinelReporter {
|
|
|
214
226
|
console.log(stylePrimary("Sentinel diagnosis"));
|
|
215
227
|
console.log(styleSecondary(divider()));
|
|
216
228
|
console.log("");
|
|
217
|
-
if (failedRunHistory?.passStreakBeforeFailure && failedRunHistory.passStreakBeforeFailure > 0) {
|
|
229
|
+
if (!isSetupFailureDiagnosis && failedRunHistory?.passStreakBeforeFailure && failedRunHistory.passStreakBeforeFailure > 0) {
|
|
218
230
|
console.log(formatCliLine(`NEW FAILURE after ${failedRunHistory.passStreakBeforeFailure} passing runs`));
|
|
219
231
|
console.log("");
|
|
220
232
|
}
|
|
221
|
-
else if ((failedRunHistory?.recurringCount || 0) > 0) {
|
|
233
|
+
else if (!isSetupFailureDiagnosis && (failedRunHistory?.recurringCount || 0) > 0) {
|
|
222
234
|
console.log(formatCliLine(`RECURRING FAILURE (${failedRunHistory?.recurringCount} previous runs)`));
|
|
235
|
+
if (failedRunHistory?.isDominantRecurringIssue && failedRunHistory?.dominantRecurringIssueTitle) {
|
|
236
|
+
console.log(formatCliLine(`Impact: ${failedRunHistory.dominantRecurringIssueTitle} is the most common recent failure in local history`));
|
|
237
|
+
}
|
|
223
238
|
console.log("");
|
|
224
239
|
}
|
|
225
240
|
for (const line of quickDiagnosis.lines) {
|
|
226
241
|
console.log(formatCliLine(line));
|
|
227
242
|
}
|
|
228
243
|
console.log("");
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (failedRunHistory?.newFailures && failedRunHistory.newFailures > 0) {
|
|
232
|
-
console.log(formatCliLine(`Impact: ${failedRunHistory.newFailures} newly failing in this run`));
|
|
233
|
-
console.log("");
|
|
234
|
-
}
|
|
235
|
-
else if (failedRunHistory?.stillFailing && failedRunHistory.stillFailing > 0) {
|
|
236
|
-
console.log(formatCliLine(`Impact: ${failedRunHistory.stillFailing} tests still failing`));
|
|
244
|
+
if (!isSetupFailureDiagnosis) {
|
|
245
|
+
console.log(styleSecondary(divider()));
|
|
237
246
|
console.log("");
|
|
247
|
+
if (failedRunHistory?.newFailures && failedRunHistory.newFailures > 0) {
|
|
248
|
+
console.log(formatCliLine(`Impact: ${failedRunHistory.newFailures} newly failing in this run`));
|
|
249
|
+
console.log("");
|
|
250
|
+
}
|
|
251
|
+
else if (failedRunHistory?.stillFailing && failedRunHistory.stillFailing > 0) {
|
|
252
|
+
console.log(formatCliLine(`Impact: ${failedRunHistory.stillFailing} tests still failing`));
|
|
253
|
+
if (failedRunHistory?.isDominantRecurringIssue && failedRunHistory?.dominantRecurringIssueCount > 0) {
|
|
254
|
+
console.log(formatCliLine(`Impact: Seen in ${failedRunHistory.dominantRecurringIssueCount} recorded failed runs`));
|
|
255
|
+
}
|
|
256
|
+
console.log("");
|
|
257
|
+
}
|
|
238
258
|
}
|
|
239
259
|
}
|
|
260
|
+
if (isSetupFailureDiagnosis) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
240
263
|
console.log(styleSecondary("Uploading debug report..."));
|
|
241
264
|
console.log("");
|
|
265
|
+
let structuredDiagnosisPath = null;
|
|
266
|
+
if (quickDiagnosisStructured) {
|
|
267
|
+
try {
|
|
268
|
+
structuredDiagnosisPath = node_path_1.default.join(this.options.testResultsDir, "sentinel-structured-diagnosis.json");
|
|
269
|
+
node_fs_1.default.mkdirSync(this.options.testResultsDir, { recursive: true });
|
|
270
|
+
node_fs_1.default.writeFileSync(structuredDiagnosisPath, JSON.stringify(quickDiagnosisStructured, null, 2));
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
structuredDiagnosisPath = null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
242
276
|
const upload = (await (0, node_1.runSentinelUpload)({
|
|
243
277
|
playwrightJsonPath: this.options.playwrightJsonPath,
|
|
244
278
|
playwrightReportDir: this.options.playwrightReportDir,
|
|
@@ -248,11 +282,12 @@ class SentinelReporter {
|
|
|
248
282
|
env: {
|
|
249
283
|
SENTINEL_REPORTER_PROJECT: this.options.project || undefined,
|
|
250
284
|
SENTINEL_REPORTER_SILENT: "1",
|
|
285
|
+
SENTINEL_STRUCTURED_DIAGNOSIS_PATH: structuredDiagnosisPath || undefined,
|
|
251
286
|
SENTINEL_UPLOAD_LOCAL: usingImplicitLocalPublicMode ? "1" : process.env.SENTINEL_UPLOAD_LOCAL
|
|
252
287
|
}
|
|
253
288
|
}));
|
|
254
289
|
if (upload.exitCode !== 0) {
|
|
255
|
-
|
|
290
|
+
return;
|
|
256
291
|
}
|
|
257
292
|
if (!quickDiagnosis?.lines.length && upload.diagnosis?.lines.length) {
|
|
258
293
|
console.log(stylePrimary("Sentinel diagnosis"));
|
package/dist/runHistory.d.ts
CHANGED
|
@@ -12,6 +12,9 @@ export type FailedRunHistorySummary = {
|
|
|
12
12
|
stillFailing: number;
|
|
13
13
|
recurringCount: number;
|
|
14
14
|
recurringTitle: string | null;
|
|
15
|
+
dominantRecurringIssueTitle: string | null;
|
|
16
|
+
dominantRecurringIssueCount: number;
|
|
17
|
+
isDominantRecurringIssue: boolean;
|
|
15
18
|
};
|
|
16
19
|
export declare const buildRunDiffSummary: (playwrightJsonPath: string) => RunDiffSummary | null;
|
|
17
20
|
export declare const buildFailedRunHistorySummary: (playwrightJsonPath: string) => FailedRunHistorySummary | null;
|
package/dist/runHistory.js
CHANGED
|
@@ -204,6 +204,10 @@ const buildFailedRunHistorySummary = (playwrightJsonPath) => {
|
|
|
204
204
|
}))
|
|
205
205
|
.sort((a, b) => b.occurrences - a.occurrences);
|
|
206
206
|
const topRecurring = recurringFailures.find((item) => item.occurrences > 0) || null;
|
|
207
|
+
const topRecurringTotal = topRecurring ? topRecurring.occurrences + 1 : 0;
|
|
208
|
+
const isDominantRecurringIssue = Boolean(topRecurring &&
|
|
209
|
+
topRecurringTotal >= 3 &&
|
|
210
|
+
topRecurringTotal >= Math.ceil(Math.max(failingReporterRuns.length, 1) / 2));
|
|
207
211
|
writeSnapshot(snapshot);
|
|
208
212
|
const lines = [];
|
|
209
213
|
if (passStreakBeforeFailure > 0) {
|
|
@@ -220,6 +224,9 @@ const buildFailedRunHistorySummary = (playwrightJsonPath) => {
|
|
|
220
224
|
}
|
|
221
225
|
if (topRecurring) {
|
|
222
226
|
lines.push(`- Recurring across ${topRecurring.occurrences + 1} recorded failed runs in local history (${topRecurring.failure.title})`);
|
|
227
|
+
if (isDominantRecurringIssue) {
|
|
228
|
+
lines.push(`- This is the most common recent failure in local history`);
|
|
229
|
+
}
|
|
223
230
|
}
|
|
224
231
|
return lines.length
|
|
225
232
|
? {
|
|
@@ -230,7 +237,10 @@ const buildFailedRunHistorySummary = (playwrightJsonPath) => {
|
|
|
230
237
|
fixedTests,
|
|
231
238
|
stillFailing,
|
|
232
239
|
recurringCount: topRecurring ? topRecurring.occurrences : 0,
|
|
233
|
-
recurringTitle: topRecurring?.failure.title || null
|
|
240
|
+
recurringTitle: topRecurring?.failure.title || null,
|
|
241
|
+
dominantRecurringIssueTitle: topRecurring?.failure.title || null,
|
|
242
|
+
dominantRecurringIssueCount: topRecurringTotal,
|
|
243
|
+
isDominantRecurringIssue
|
|
234
244
|
}
|
|
235
245
|
: null;
|
|
236
246
|
};
|
package/dist/terminalSummary.js
CHANGED
|
@@ -104,6 +104,25 @@ const getPrimaryRiskKind = (candidate) => {
|
|
|
104
104
|
return "historical_failures";
|
|
105
105
|
return "generic";
|
|
106
106
|
};
|
|
107
|
+
const isStrongPassRisk = (candidate, lastFailureRunsAgo) => {
|
|
108
|
+
if (candidate.retries > 0)
|
|
109
|
+
return true;
|
|
110
|
+
if (candidate.ratio !== null && candidate.ratio >= 2.0)
|
|
111
|
+
return true;
|
|
112
|
+
if (candidate.repeatedSlowPasses >= 3)
|
|
113
|
+
return true;
|
|
114
|
+
if (candidate.timeoutUtilization !== null && candidate.timeoutUtilization >= 0.9)
|
|
115
|
+
return true;
|
|
116
|
+
if (candidate.historicalRetries >= 2)
|
|
117
|
+
return true;
|
|
118
|
+
if (candidate.historicalFailures >= 6 &&
|
|
119
|
+
lastFailureRunsAgo !== null &&
|
|
120
|
+
lastFailureRunsAgo <= 2 &&
|
|
121
|
+
(candidate.ratio !== null || candidate.historicalRetries > 0 || candidate.repeatedSlowPasses > 0)) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
};
|
|
107
126
|
const cleanTitlePath = (parts) => {
|
|
108
127
|
const normalized = parts.map((part) => part.trim()).filter(Boolean);
|
|
109
128
|
const withoutUnnamed = normalized.filter((part) => part !== "Unnamed test");
|
|
@@ -339,6 +358,22 @@ const buildPassingRunSummary = (playwrightJsonPath, options) => {
|
|
|
339
358
|
: recentFailurePressure
|
|
340
359
|
? `failed in ${historicalFailures} of the last 10 runs`
|
|
341
360
|
: `has weak instability signals`;
|
|
361
|
+
const riskKind = getPrimaryRiskKind({
|
|
362
|
+
title: test.title,
|
|
363
|
+
ratio,
|
|
364
|
+
recentTrendRatio,
|
|
365
|
+
retries: test.retries,
|
|
366
|
+
historicalRetries,
|
|
367
|
+
historicalFailures,
|
|
368
|
+
repeatedSlowPasses,
|
|
369
|
+
timeoutUtilization,
|
|
370
|
+
medianDurationMs: med,
|
|
371
|
+
currentDurationMs: test.durationMs,
|
|
372
|
+
score,
|
|
373
|
+
strongSignal,
|
|
374
|
+
currentRunSignal,
|
|
375
|
+
primaryReason
|
|
376
|
+
});
|
|
342
377
|
if (score > 0) {
|
|
343
378
|
nearFailureCandidates.push({
|
|
344
379
|
title: test.title,
|
|
@@ -354,7 +389,8 @@ const buildPassingRunSummary = (playwrightJsonPath, options) => {
|
|
|
354
389
|
score,
|
|
355
390
|
strongSignal,
|
|
356
391
|
currentRunSignal,
|
|
357
|
-
primaryReason
|
|
392
|
+
primaryReason,
|
|
393
|
+
riskKind
|
|
358
394
|
});
|
|
359
395
|
}
|
|
360
396
|
}
|
|
@@ -364,20 +400,16 @@ const buildPassingRunSummary = (playwrightJsonPath, options) => {
|
|
|
364
400
|
(right.timeoutUtilization || 0) - (left.timeoutUtilization || 0) ||
|
|
365
401
|
(right.ratio || 0) - (left.ratio || 0));
|
|
366
402
|
const strongNearFailures = nearFailureCandidates.filter((candidate) => candidate.strongSignal &&
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
candidate.score >= 3);
|
|
372
|
-
const topNearFailures = strongNearFailures.slice(0, 2);
|
|
373
|
-
const flakyLookingCount = strongNearFailures.length;
|
|
374
|
-
const hasActiveRisk = flakyLookingCount > 0;
|
|
403
|
+
candidate.score >= 3 &&
|
|
404
|
+
isStrongPassRisk(candidate, lastFailureRunsAgo));
|
|
405
|
+
const topRisk = strongNearFailures[0] || null;
|
|
406
|
+
const hasActiveRisk = Boolean(topRisk);
|
|
375
407
|
const totalDurationMs = snapshot.tests.reduce((sum, test) => sum + test.durationMs, 0);
|
|
376
408
|
const displayedRunDurationMs = options?.observedRunDurationMs || snapshot.wallDurationMs || totalDurationMs;
|
|
377
409
|
const lines = ["All tests passed", `${snapshot.passedCount} tests in ${formatDuration(displayedRunDurationMs)}`];
|
|
378
|
-
if (hasActiveRisk &&
|
|
379
|
-
const riskKind =
|
|
380
|
-
lines.push(`At risk: ${
|
|
410
|
+
if (hasActiveRisk && topRisk) {
|
|
411
|
+
const riskKind = topRisk.riskKind;
|
|
412
|
+
lines.push(`At risk: ${topRisk.title} ${topRisk.primaryReason}`);
|
|
381
413
|
if (riskKind === "retry" || riskKind === "historical_retries") {
|
|
382
414
|
lines.push("Why this matters: Similar retry patterns often turn into flaky failures");
|
|
383
415
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentinelqa/playwright-reporter",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.54",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Playwright reporter for CI debugging with optional Sentinel cloud dashboards",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"@playwright/test": ">=1.40.0"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@sentinelqa/uploader": "^0.1.
|
|
42
|
+
"@sentinelqa/uploader": "^0.1.38"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^20.19.32",
|