@validate.qa/runner 1.0.100 → 1.0.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-2OKZY3WN.mjs +291 -0
- package/dist/{chunk-5P4ANVDY.mjs → chunk-4SZC5ZX3.mjs} +5092 -760
- package/dist/{chunk-2XD63NU5.mjs → chunk-BBTK6OX6.mjs} +567 -84
- package/dist/{chunk-WDXLAZ2J.mjs → chunk-CO5KHIVX.mjs} +3 -2
- package/dist/{chunk-HMUC3DKS.mjs → chunk-CYWHPXNZ.mjs} +2281 -2
- package/dist/{chunk-MAO6ADAO.mjs → chunk-FQUH2PU2.mjs} +1 -1
- package/dist/{chunk-MBKGTELT.mjs → chunk-IGXGSDE3.mjs} +701 -283
- package/dist/{chunk-II7QW7MZ.mjs → chunk-M3VZGHZQ.mjs} +14 -11
- package/dist/{chunk-GIIIVO2W.mjs → chunk-SN3PN4G4.mjs} +151 -11
- package/dist/{chunk-6NIT2CCH.mjs → chunk-TPU42VH2.mjs} +10 -9
- package/dist/{chunk-MHFP3QXU.mjs → chunk-XZUUBMP3.mjs} +1 -1
- package/dist/cli.js +8994 -1015
- package/dist/cli.mjs +211 -170
- package/dist/credential-mcp-server.js +503 -79
- package/dist/credential-mcp-server.mjs +3 -3
- package/dist/discovery-mcp-server.js +654 -87
- package/dist/discovery-mcp-server.mjs +4 -4
- package/dist/dist-VTPBY2C4.mjs +17 -0
- package/dist/inbox-mcp-server.js +496 -73
- package/dist/inbox-mcp-server.mjs +3 -3
- package/dist/route-seed-worker.js +2780 -74
- package/dist/route-seed-worker.mjs +14 -7
- package/dist/run-write-guard-preload.js +1206 -160
- package/dist/run-write-guard-preload.mjs +5 -285
- package/dist/run-write-guard.js +1206 -160
- package/dist/run-write-guard.mjs +3 -3
- package/package.json +3 -3
- package/dist/dist-DFNLMH6L.mjs +0 -16
|
@@ -3027,7 +3027,7 @@ function unescapeQuotedBody(raw) {
|
|
|
3027
3027
|
return raw.replace(/\\(['"`\\])/g, "$1");
|
|
3028
3028
|
}
|
|
3029
3029
|
function extractRoleNameValue(line) {
|
|
3030
|
-
const m = line.match(new RegExp(`getByRole\\s*\\([^,]
|
|
3030
|
+
const m = line.match(new RegExp(`getByRole\\s*\\([^,]{1,200},\\s*\\{[^}]{0,200}?name\\s*:\\s*(?:${QUOTED_BODY}|\\/([^/]*?)\\/)`, "i"));
|
|
3031
3031
|
if (!m)
|
|
3032
3032
|
return null;
|
|
3033
3033
|
const raw = m[1] ?? m[2] ?? m[3];
|
|
@@ -3735,6 +3735,8 @@ function stripCoverageComments(code) {
|
|
|
3735
3735
|
let copyFrom = 0;
|
|
3736
3736
|
let i = 0;
|
|
3737
3737
|
let quote = null;
|
|
3738
|
+
let prev = "";
|
|
3739
|
+
let noRegexBefore = 0;
|
|
3738
3740
|
while (i < code.length) {
|
|
3739
3741
|
const ch = code[i];
|
|
3740
3742
|
if (quote) {
|
|
@@ -3742,12 +3744,24 @@ function stripCoverageComments(code) {
|
|
|
3742
3744
|
i += 2;
|
|
3743
3745
|
continue;
|
|
3744
3746
|
}
|
|
3745
|
-
if (ch === quote || ch === "\n" && quote !== "`")
|
|
3747
|
+
if (ch === quote || ch === "\n" && quote !== "`") {
|
|
3746
3748
|
quote = null;
|
|
3749
|
+
prev = ch === "\n" ? prev : ch;
|
|
3750
|
+
}
|
|
3747
3751
|
i++;
|
|
3748
3752
|
continue;
|
|
3749
3753
|
}
|
|
3750
3754
|
const next = code[i + 1];
|
|
3755
|
+
if (ch === "/" && next !== "*" && next !== "/" && i >= noRegexBefore && (prev === "" || REGEX_PRECEDER2.includes(prev))) {
|
|
3756
|
+
const end = regexLiteralEnd3(code, i);
|
|
3757
|
+
if (end > 0) {
|
|
3758
|
+
i = end;
|
|
3759
|
+
prev = "a";
|
|
3760
|
+
continue;
|
|
3761
|
+
}
|
|
3762
|
+
const eol = code.indexOf("\n", i);
|
|
3763
|
+
noRegexBefore = eol === -1 ? code.length : eol;
|
|
3764
|
+
}
|
|
3751
3765
|
if (ch === "/" && next === "*") {
|
|
3752
3766
|
const end = code.indexOf("*/", i + 2);
|
|
3753
3767
|
const stop = end === -1 ? code.length : end + 2;
|
|
@@ -3766,11 +3780,39 @@ function stripCoverageComments(code) {
|
|
|
3766
3780
|
}
|
|
3767
3781
|
if (ch === '"' || ch === "'" || ch === "`")
|
|
3768
3782
|
quote = ch;
|
|
3783
|
+
if (ch !== " " && ch !== " " && ch !== "\n" && ch !== "\r")
|
|
3784
|
+
prev = ch;
|
|
3769
3785
|
i++;
|
|
3770
3786
|
}
|
|
3771
3787
|
out.push(code.slice(copyFrom));
|
|
3772
3788
|
return out.join("");
|
|
3773
3789
|
}
|
|
3790
|
+
function regexLiteralEnd3(code, start) {
|
|
3791
|
+
let inClass = false;
|
|
3792
|
+
for (let j = start + 1; j < code.length; j++) {
|
|
3793
|
+
const c = code[j];
|
|
3794
|
+
if (c === "\n")
|
|
3795
|
+
return -1;
|
|
3796
|
+
if (c === "\\") {
|
|
3797
|
+
j++;
|
|
3798
|
+
continue;
|
|
3799
|
+
}
|
|
3800
|
+
if (inClass) {
|
|
3801
|
+
if (c === "]")
|
|
3802
|
+
inClass = false;
|
|
3803
|
+
continue;
|
|
3804
|
+
}
|
|
3805
|
+
if (c === "[")
|
|
3806
|
+
inClass = true;
|
|
3807
|
+
else if (c === "/") {
|
|
3808
|
+
let k = j + 1;
|
|
3809
|
+
while (k < code.length && isIdentChar(code[k]))
|
|
3810
|
+
k++;
|
|
3811
|
+
return k;
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3814
|
+
return -1;
|
|
3815
|
+
}
|
|
3774
3816
|
function isIdentChar(ch) {
|
|
3775
3817
|
if (!ch)
|
|
3776
3818
|
return false;
|
|
@@ -4428,7 +4470,7 @@ function assessTitleCoverage(title, code) {
|
|
|
4428
4470
|
const repeatedHit = [...repeated].some((term) => hay.includes(term));
|
|
4429
4471
|
return { covered: hit >= needed || repeatedHit, missingTerms };
|
|
4430
4472
|
}
|
|
4431
|
-
var COVERAGE_STOP_WORDS, MAX_ASSERTION_JOIN_CHARS, MAX_COVERAGE_LINE_CHARS, BLOCK_SCAFFOLD, cachedAssertionInput, cachedAssertionSource, LOCAL_BINDING, ASSERTED_STATUS_RE, MAX_RESOLVED_NAMES_PER_LINE, WORD_CHAR_RE, NOT_CONTAIN_TEXT_RE, PLACE_PHRASE_RE, PLACE_WORD_MIN_CHARS, PLACE_FUNCTION_WORDS, HIDDEN_STATE_TERMS, HIDDEN_ELEMENT_LINE_RE, HIDDEN_LINE_RE, MAX_SHOWN_EARLIER_LINES, SHOWN_EARLIER_LITERAL_RE, SHARED_PREFIX_MIN_CHARS, MAX_SHOWN_EARLIER_LITERALS, VALUE_SHAPE_RE, VALUE_SHAPE_ALL_RE, TITLE_TERM_MIN;
|
|
4473
|
+
var COVERAGE_STOP_WORDS, MAX_ASSERTION_JOIN_CHARS, MAX_COVERAGE_LINE_CHARS, REGEX_PRECEDER2, BLOCK_SCAFFOLD, cachedAssertionInput, cachedAssertionSource, LOCAL_BINDING, ASSERTED_STATUS_RE, MAX_RESOLVED_NAMES_PER_LINE, WORD_CHAR_RE, NOT_CONTAIN_TEXT_RE, PLACE_PHRASE_RE, PLACE_WORD_MIN_CHARS, PLACE_FUNCTION_WORDS, HIDDEN_STATE_TERMS, HIDDEN_ELEMENT_LINE_RE, HIDDEN_LINE_RE, MAX_SHOWN_EARLIER_LINES, SHOWN_EARLIER_LITERAL_RE, SHARED_PREFIX_MIN_CHARS, MAX_SHOWN_EARLIER_LITERALS, VALUE_SHAPE_RE, VALUE_SHAPE_ALL_RE, TITLE_TERM_MIN;
|
|
4432
4474
|
var init_expected_outcome_coverage = __esm({
|
|
4433
4475
|
"../shared/dist/expected-outcome-coverage.js"() {
|
|
4434
4476
|
"use strict";
|
|
@@ -4465,6 +4507,7 @@ var init_expected_outcome_coverage = __esm({
|
|
|
4465
4507
|
]);
|
|
4466
4508
|
MAX_ASSERTION_JOIN_CHARS = 8e3;
|
|
4467
4509
|
MAX_COVERAGE_LINE_CHARS = 2e3;
|
|
4510
|
+
REGEX_PRECEDER2 = "(,=:[!&|?{};+-*%<>~^";
|
|
4468
4511
|
BLOCK_SCAFFOLD = /=>|\b(?:test|describe|it|beforeEach|afterEach|beforeAll|afterAll|function)\b/;
|
|
4469
4512
|
cachedAssertionInput = null;
|
|
4470
4513
|
cachedAssertionSource = { raw: "", normalized: "", bindings: /* @__PURE__ */ new Map(), bindingRefs: /* @__PURE__ */ new Map() };
|
|
@@ -4915,7 +4958,7 @@ function detectsErrorBannerAssertion(code) {
|
|
|
4915
4958
|
const cleaned = stripCommentsAndLogs(stripLoginScaffolding(code));
|
|
4916
4959
|
const lines = cleaned.split("\n");
|
|
4917
4960
|
const candidates = [];
|
|
4918
|
-
const positive = (stmt) => !
|
|
4961
|
+
const positive = (stmt) => !claimsAbsence(stmt);
|
|
4919
4962
|
const statements = liveExpectStatements(cleaned).map((s) => s.stmt);
|
|
4920
4963
|
for (const stmt of statements) {
|
|
4921
4964
|
if (positive(stmt))
|
|
@@ -5044,6 +5087,12 @@ function detectsObservedFailureAsExpected(code) {
|
|
|
5044
5087
|
return FAILURE_STATUS_ASSERT.some((re) => re.test(stmt)) || hasFailedListener && FAILED_LIST_ASSERT.test(stmt);
|
|
5045
5088
|
});
|
|
5046
5089
|
}
|
|
5090
|
+
function claimsAbsence(stmt) {
|
|
5091
|
+
const negated = NEGATED_ASSERT_NOT.test(stmt);
|
|
5092
|
+
const bare = negated ? stmt.replace(NOT_BEFORE_MATCHER, ".") : stmt;
|
|
5093
|
+
const absenceMatcher = /\.toBeHidden\s*\(|\.toHaveCount\s*\(\s*0\s*\)/.test(bare) || VALUE_ABSENCE.test(bare) || OPTION_ABSENCE.test(bare);
|
|
5094
|
+
return negated !== absenceMatcher;
|
|
5095
|
+
}
|
|
5047
5096
|
function isVacuousErrorAbsence(stmt, presence) {
|
|
5048
5097
|
if (!ABSENCE_MATCHER.test(stmt))
|
|
5049
5098
|
return false;
|
|
@@ -5059,19 +5108,53 @@ function isChromeTarget(stmt) {
|
|
|
5059
5108
|
return CHROME_LOCATOR.test(stmt) && !CONTENT_ROLE.test(stmt) && !/getByText\s*\(/.test(stmt);
|
|
5060
5109
|
}
|
|
5061
5110
|
function countsAsContentText(stmt) {
|
|
5062
|
-
if (!TEXT_CONTENT_MATCHER.test(stmt))
|
|
5111
|
+
if (!TEXT_CONTENT_MATCHER.test(stmt) || NEGATED_ASSERT.test(stmt))
|
|
5063
5112
|
return false;
|
|
5064
5113
|
if (isSymbolOnlyArgument(stmt, TEXT_CONTENT_MATCHER))
|
|
5065
5114
|
return false;
|
|
5066
|
-
|
|
5115
|
+
return onContentTarget(stmt);
|
|
5116
|
+
}
|
|
5117
|
+
function onContentTarget(stmt) {
|
|
5118
|
+
return !isChromeTarget(stmt) || /getByRole\s*\(\s*['"`]main['"`]/i.test(stmt);
|
|
5119
|
+
}
|
|
5120
|
+
function provesTextRemoved(stmt, presence) {
|
|
5121
|
+
const m = stmt.match(NEGATED_TEXT);
|
|
5122
|
+
if (!m || !onContentTarget(stmt))
|
|
5123
|
+
return false;
|
|
5124
|
+
const text = m[1] ?? m[2];
|
|
5125
|
+
if (text === void 0)
|
|
5067
5126
|
return true;
|
|
5068
|
-
|
|
5127
|
+
if (!/[\p{L}\p{N}]/u.test(text))
|
|
5128
|
+
return false;
|
|
5129
|
+
return !ERROR_TEXT_WORDS.test(text) || presence().includes(text);
|
|
5069
5130
|
}
|
|
5070
5131
|
function isDataContentRole(stmt) {
|
|
5071
5132
|
return CONTENT_ROLE.test(stmt) && !ANNOUNCE_ROLE.test(stmt);
|
|
5072
5133
|
}
|
|
5134
|
+
function destructuresRequest(code) {
|
|
5135
|
+
let readTo = -1;
|
|
5136
|
+
for (const m of code.matchAll(ASYNC_DESTRUCTURE_OPEN)) {
|
|
5137
|
+
const start = m.index + m[0].length;
|
|
5138
|
+
if (start <= readTo)
|
|
5139
|
+
continue;
|
|
5140
|
+
let end = start;
|
|
5141
|
+
while (end < code.length && code[end] !== "}" && code[end] !== ")")
|
|
5142
|
+
end++;
|
|
5143
|
+
readTo = end;
|
|
5144
|
+
if (end === code.length)
|
|
5145
|
+
return false;
|
|
5146
|
+
if (code[end] !== "}" || !REQUEST_WORD.test(code.slice(start, end)))
|
|
5147
|
+
continue;
|
|
5148
|
+
let close = end + 1;
|
|
5149
|
+
while (close < code.length && /\s/.test(code[close]))
|
|
5150
|
+
close++;
|
|
5151
|
+
if (code[close] === ")")
|
|
5152
|
+
return true;
|
|
5153
|
+
}
|
|
5154
|
+
return false;
|
|
5155
|
+
}
|
|
5073
5156
|
function isApiTest(code) {
|
|
5074
|
-
const usesRequestFixture =
|
|
5157
|
+
const usesRequestFixture = destructuresRequest(code) || /\brequest\.(get|post|put|patch|delete|head|fetch)\s*\(/.test(code);
|
|
5075
5158
|
if (!usesRequestFixture)
|
|
5076
5159
|
return false;
|
|
5077
5160
|
const drivesUi = /\bpage\.(getBy\w+|locator|click|fill|goto|waitForURL|reload)\s*\(/.test(code) || /\.(click|fill|check|uncheck|selectOption|pressSequentially)\s*\(/.test(code);
|
|
@@ -5330,6 +5413,9 @@ function hasOnlyConstantAssertions(code) {
|
|
|
5330
5413
|
const { constant, real } = constantAssertions(code);
|
|
5331
5414
|
return constant.length > 0 && real === 0;
|
|
5332
5415
|
}
|
|
5416
|
+
function envValueKey(name) {
|
|
5417
|
+
return `\0env:${name}`;
|
|
5418
|
+
}
|
|
5333
5419
|
function enteredValues(stmts) {
|
|
5334
5420
|
const values = /* @__PURE__ */ new Map();
|
|
5335
5421
|
const navLines = [];
|
|
@@ -5338,24 +5424,55 @@ function enteredValues(stmts) {
|
|
|
5338
5424
|
navLines.push(startLine);
|
|
5339
5425
|
let m;
|
|
5340
5426
|
ENTERED_VALUE.lastIndex = 0;
|
|
5341
|
-
|
|
5342
|
-
const
|
|
5343
|
-
if (!
|
|
5344
|
-
values.set(value, startLine);
|
|
5345
|
-
|
|
5427
|
+
const add = (value) => {
|
|
5428
|
+
const lines = values.get(value);
|
|
5429
|
+
if (!lines)
|
|
5430
|
+
values.set(value, [startLine]);
|
|
5431
|
+
else if (lines[lines.length - 1] !== startLine)
|
|
5432
|
+
lines.push(startLine);
|
|
5433
|
+
};
|
|
5434
|
+
while (m = ENTERED_VALUE.exec(stmt))
|
|
5435
|
+
add((m[1] ?? m[2] ?? m[3]).toLowerCase());
|
|
5436
|
+
ENTERED_ENV.lastIndex = 0;
|
|
5437
|
+
while (m = ENTERED_ENV.exec(stmt))
|
|
5438
|
+
add(envValueKey(m[1] ?? m[2] ?? m[3] ?? m[4]));
|
|
5346
5439
|
}
|
|
5347
5440
|
return { values, navLines };
|
|
5348
5441
|
}
|
|
5349
|
-
function
|
|
5442
|
+
function countBelow(lines, line) {
|
|
5443
|
+
let lo = 0;
|
|
5444
|
+
let hi = lines.length;
|
|
5445
|
+
while (lo < hi) {
|
|
5446
|
+
const mid = lo + hi >> 1;
|
|
5447
|
+
if (lines[mid] < line)
|
|
5448
|
+
lo = mid + 1;
|
|
5449
|
+
else
|
|
5450
|
+
hi = mid;
|
|
5451
|
+
}
|
|
5452
|
+
return lo;
|
|
5453
|
+
}
|
|
5454
|
+
function enteredLinesOf(stmt, entered) {
|
|
5350
5455
|
const m = stmt.match(/\.toHaveValue\s*\(\s*(['"`])((?:(?!\1).)+)\1/);
|
|
5351
|
-
if (
|
|
5456
|
+
if (m)
|
|
5457
|
+
return entered.values.get(m[2].toLowerCase());
|
|
5458
|
+
const env = stmt.match(HAVE_VALUE_ENV);
|
|
5459
|
+
return env ? entered.values.get(envValueKey(env[1] ?? env[2])) : void 0;
|
|
5460
|
+
}
|
|
5461
|
+
function typedSinceLastNav(stmt, entered, startLine, lines = enteredLinesOf(stmt, entered)) {
|
|
5462
|
+
if (!lines || startLine === void 0)
|
|
5352
5463
|
return false;
|
|
5353
|
-
const
|
|
5354
|
-
if (
|
|
5464
|
+
const before = countBelow(lines, startLine);
|
|
5465
|
+
if (before === 0)
|
|
5355
5466
|
return false;
|
|
5356
|
-
|
|
5467
|
+
return countBelow(entered.navLines, startLine) === countBelow(entered.navLines, lines[before - 1] + 1);
|
|
5468
|
+
}
|
|
5469
|
+
function isEnteredValueEcho(stmt, entered, startLine) {
|
|
5470
|
+
const lines = enteredLinesOf(stmt, entered);
|
|
5471
|
+
if (!lines)
|
|
5472
|
+
return false;
|
|
5473
|
+
if (startLine === void 0 || lines[0] >= startLine)
|
|
5357
5474
|
return true;
|
|
5358
|
-
return
|
|
5475
|
+
return typedSinceLastNav(stmt, entered, startLine, lines);
|
|
5359
5476
|
}
|
|
5360
5477
|
function isAnyValueMatcher(stmt) {
|
|
5361
5478
|
if (/\.not\.toHaveValue\s*\(\s*(?:['"`]{2}|\/\^?\$?\/)/.test(stmt))
|
|
@@ -5445,6 +5562,43 @@ function hasAttributeTransition(stmts) {
|
|
|
5445
5562
|
return true;
|
|
5446
5563
|
return false;
|
|
5447
5564
|
}
|
|
5565
|
+
function comparesWithItself(subject, tail) {
|
|
5566
|
+
const m = SELF_COMPARE_MATCHER.exec(tail);
|
|
5567
|
+
if (!m)
|
|
5568
|
+
return false;
|
|
5569
|
+
const args = balancedArguments(tail, m[0].length, MATCHER_ARGS_MAX);
|
|
5570
|
+
if (args === null)
|
|
5571
|
+
return false;
|
|
5572
|
+
const squash2 = (x) => x.replace(/^\s*await\s+/, "").replace(/\s+/g, "");
|
|
5573
|
+
const expected = squash2(args);
|
|
5574
|
+
return expected === squash2(m[1] === "toHaveCount" ? `${subject}.count()` : subject);
|
|
5575
|
+
}
|
|
5576
|
+
function asPlainAssertion(s) {
|
|
5577
|
+
const { stmt } = s;
|
|
5578
|
+
if (COUNT_AT_LEAST_ZERO.test(stmt))
|
|
5579
|
+
return [];
|
|
5580
|
+
const subject = extractExpectSubject(stmt);
|
|
5581
|
+
if (subject === null)
|
|
5582
|
+
return [s];
|
|
5583
|
+
const plain = subject.replace(/^await\s+/, "").trim();
|
|
5584
|
+
const subjectAt = stmt.indexOf(subject, stmt.search(EXPECT_CALL_RE));
|
|
5585
|
+
const tail = stmt.slice(subjectAt + subject.length).trimStart();
|
|
5586
|
+
const as = (rewritten) => [{ ...s, stmt: rewritten }];
|
|
5587
|
+
if (LOCATOR_EXPR.test(plain) && !LOCATOR_VALUE_READ.test(plain) && ALWAYS_TRUE_ON_OBJECT.test(tail))
|
|
5588
|
+
return [];
|
|
5589
|
+
if (DEFINED_VALUE_READ.test(plain) && ALWAYS_DEFINED.test(tail))
|
|
5590
|
+
return [];
|
|
5591
|
+
if (comparesWithItself(plain, tail))
|
|
5592
|
+
return [];
|
|
5593
|
+
const visible = /^(.+)\.\s*isVisible\s*\(\s*\)$/s.exec(plain);
|
|
5594
|
+
if (visible && IS_VISIBLE_TRUE.test(tail))
|
|
5595
|
+
return as(`await expect(${visible[1]}).toBeVisible();`);
|
|
5596
|
+
if (/^page\s*\.\s*(?:url|title)\s*\(\s*\)$/.test(plain) && PAGE_VALUE_MATCHER.test(tail)) {
|
|
5597
|
+
const matcher = plain.includes("url") ? "toHaveURL" : "toHaveTitle";
|
|
5598
|
+
return as(`await expect(page)${tail.replace(/^\)\s*\.\s*\w+\s*\(/, `.${matcher}(`)}`);
|
|
5599
|
+
}
|
|
5600
|
+
return [s];
|
|
5601
|
+
}
|
|
5448
5602
|
function creditsOutcomeAlways(stmt, entered, startLine) {
|
|
5449
5603
|
if (!OUTCOME_ALWAYS.some((re) => re.test(stmt)))
|
|
5450
5604
|
return false;
|
|
@@ -5518,6 +5672,8 @@ function classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstM
|
|
|
5518
5672
|
continue;
|
|
5519
5673
|
if (isVacuousErrorAbsence(stmt, presence))
|
|
5520
5674
|
continue;
|
|
5675
|
+
if (COUNT_ONE.test(stmt) && quotedAssertedStrings(stmt).some((t) => clicked.has(t.toLowerCase())))
|
|
5676
|
+
continue;
|
|
5521
5677
|
if (creditsOutcomeAlways(stmt, entered, startLine))
|
|
5522
5678
|
return { kind: "durable" };
|
|
5523
5679
|
if (mutated && !NEGATED_ASSERT.test(stmt) && MESSAGE_SURFACE.test(stmt) && OUTCOME_TEXT.test(assertedText(stmt))) {
|
|
@@ -5527,12 +5683,15 @@ function classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstM
|
|
|
5527
5683
|
else
|
|
5528
5684
|
return { kind: "durable" };
|
|
5529
5685
|
}
|
|
5530
|
-
|
|
5686
|
+
const retyped = typedSinceLastNav(stmt, entered, startLine);
|
|
5687
|
+
if (!retyped && STATE_MATCHER.test(stmt) && (persistence && startLine !== void 0 ? reopenedAfterSave(startLine, persistence, persistence.reloadCalls) : reloaded))
|
|
5531
5688
|
return { kind: "durable" };
|
|
5532
|
-
if (provesStoredControlValue(stmt, startLine, persistence))
|
|
5689
|
+
if (!retyped && provesStoredControlValue(stmt, startLine, persistence))
|
|
5533
5690
|
return { kind: "durable" };
|
|
5534
5691
|
if (countsAsContentText(stmt))
|
|
5535
5692
|
return { kind: "durable" };
|
|
5693
|
+
if (provesTextRemoved(stmt, presence))
|
|
5694
|
+
return { kind: "durable" };
|
|
5536
5695
|
if (mutated && VISIBILITY_MATCHER.test(stmt)) {
|
|
5537
5696
|
const revealsText = GET_BY_TEXT.test(stmt) || HAS_TEXT_FILTER.test(stmt) || CONTENT_ROLE.test(stmt);
|
|
5538
5697
|
if (revealsText && !isChromeTarget(stmt)) {
|
|
@@ -5599,7 +5758,7 @@ function detectOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstMut
|
|
|
5599
5758
|
function validateAssertionStrength(code, opts = {}) {
|
|
5600
5759
|
const body = stripLoginScaffolding(code);
|
|
5601
5760
|
const lines = body.split("\n");
|
|
5602
|
-
const stmts = liveExpectStatements(stripCoverageComments(body));
|
|
5761
|
+
const stmts = liveExpectStatements(stripCoverageComments(body)).flatMap(asPlainAssertion);
|
|
5603
5762
|
const base = (extra) => ({
|
|
5604
5763
|
apiTest: false,
|
|
5605
5764
|
interacted: false,
|
|
@@ -5805,7 +5964,7 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
5805
5964
|
signals
|
|
5806
5965
|
};
|
|
5807
5966
|
}
|
|
5808
|
-
var ERROR_BANNER, OUTCOME_ALWAYS, FORM_INPUT, ANY_CLICK, NAV_LOCATOR, URL_PROOF, RELOAD, EXPECT_STMT, BLOCK_SCAFFOLD2, TEST_BLOCK_OPEN, LOCATOR_BINDING, EXPECT_OPEN, STATE_MATCHER, CHROME_LOCATOR, CONTENT_ROLE, GET_BY_TEXT, BARE_CONTAINER_ROLE, HAS_TEXT_FILTER, OUTCOME_TEXT, SUCCESS_NOTICE_TEXT, NOT_A_SUCCESS_NOTICE, ANNOUNCE_ROLE, MESSAGE_SURFACE, TEXT_CONTENT_MATCHER, VISIBILITY_MATCHER, ADDITIVE_AFFORDANCE, EXCLUDE_AFFORDANCE, EMPTY_STATE_TEXT, AUTH_START, AUTH_END, LOGIN_CALL_LINE, INBOX_START, INBOX_END, NEGATED_ASSERT, CONST_CHAIN_MAX_DEPTH, TYPED_INPUT, TITLE_LINE, FAILURE_STATUS_ASSERT, FAILED_LIST_ASSERT, ENDPOINT_DOWN, NAVIGATION_RESPONSE_VAR, DIRECT_REQUEST_VAR, CATCH_ALL_PATTERN, GUARD_HANDLER, ABSENCE_MATCHER, ERROR_TEXT_WORDS, TEXT_REGEX_ARG, ROLE_NAME_RE, INTERACT_VERB, ROLE_SIG_RE, TRANSIENT_CONTROL_ROLES, POLL_CALLBACK, CONSTANT_WORD, CONSTANT_GLUE_CHARS, WORD_CHAR, MATCHER_ARGS_MAX, MATCHER_OPEN, ENTERED_VALUE, PAGE_NAV, IN_FLIGHT_TEXT, FORM_CONTROL_LOCATOR, CONTROL_VALUE_MATCHER, COPY_CONTROL, COPIED_NOTICE, SEND_CONTROL, SEND_NOTICE, MARK_READ_CONTROL, APP_DATA_FIELD;
|
|
5967
|
+
var ERROR_BANNER, OUTCOME_ALWAYS, FORM_INPUT, ANY_CLICK, NAV_LOCATOR, URL_PROOF, RELOAD, EXPECT_STMT, BLOCK_SCAFFOLD2, TEST_BLOCK_OPEN, LOCATOR_BINDING, EXPECT_OPEN, STATE_MATCHER, CHROME_LOCATOR, CONTENT_ROLE, GET_BY_TEXT, BARE_CONTAINER_ROLE, HAS_TEXT_FILTER, OUTCOME_TEXT, SUCCESS_NOTICE_TEXT, NOT_A_SUCCESS_NOTICE, ANNOUNCE_ROLE, MESSAGE_SURFACE, TEXT_CONTENT_MATCHER, VISIBILITY_MATCHER, ADDITIVE_AFFORDANCE, EXCLUDE_AFFORDANCE, EMPTY_STATE_TEXT, AUTH_START, AUTH_END, LOGIN_CALL_LINE, INBOX_START, INBOX_END, NEGATED_ASSERT, NEGATED_ASSERT_NOT, CONST_CHAIN_MAX_DEPTH, TYPED_INPUT, TITLE_LINE, FAILURE_STATUS_ASSERT, FAILED_LIST_ASSERT, ENDPOINT_DOWN, NAVIGATION_RESPONSE_VAR, DIRECT_REQUEST_VAR, CATCH_ALL_PATTERN, GUARD_HANDLER, ABSENCE_MATCHER, VALUE_ABSENCE, OPTION_ABSENCE, NOT_BEFORE_MATCHER, ERROR_TEXT_WORDS, TEXT_REGEX_ARG, NEGATED_TEXT, ASYNC_DESTRUCTURE_OPEN, REQUEST_WORD, ROLE_NAME_RE, INTERACT_VERB, ROLE_SIG_RE, TRANSIENT_CONTROL_ROLES, POLL_CALLBACK, CONSTANT_WORD, CONSTANT_GLUE_CHARS, WORD_CHAR, MATCHER_ARGS_MAX, MATCHER_OPEN, ENTERED_VALUE, PAGE_NAV, ENV_READ, ENTERED_ENV, HAVE_VALUE_ENV, IN_FLIGHT_TEXT, LOCATOR_VALUE_READ, LOCATOR_EXPR, ALWAYS_TRUE_ON_OBJECT, COUNT_AT_LEAST_ZERO, DEFINED_VALUE_READ, ALWAYS_DEFINED, SELF_COMPARE_MATCHER, IS_VISIBLE_TRUE, PAGE_VALUE_MATCHER, COUNT_ONE, FORM_CONTROL_LOCATOR, CONTROL_VALUE_MATCHER, COPY_CONTROL, COPIED_NOTICE, SEND_CONTROL, SEND_NOTICE, MARK_READ_CONTROL, APP_DATA_FIELD;
|
|
5809
5968
|
var init_assertion_strength = __esm({
|
|
5810
5969
|
"../shared/dist/assertion-strength.js"() {
|
|
5811
5970
|
"use strict";
|
|
@@ -5830,8 +5989,8 @@ var init_assertion_strength = __esm({
|
|
|
5830
5989
|
// form field value
|
|
5831
5990
|
/\.toMatch\s*\(/,
|
|
5832
5991
|
// string/regex content match (e.g. textContent() vs /[2-9] selected/)
|
|
5833
|
-
|
|
5834
|
-
// a control became DISABLED — a gating/validation outcome
|
|
5992
|
+
/(?<!\.not\s*)\.\s*toBeDisabled\s*\(/,
|
|
5993
|
+
// a control became DISABLED — a gating/validation outcome (not.toBeDisabled is the toBeEnabled twin)
|
|
5835
5994
|
/\.not\.toBeVisible\s*\(/,
|
|
5836
5995
|
/\.toBeHidden\s*\(/
|
|
5837
5996
|
// something the action removed/closed (disappearance proof)
|
|
@@ -5839,7 +5998,7 @@ var init_assertion_strength = __esm({
|
|
|
5839
5998
|
FORM_INPUT = /\.(fill|selectOption|setInputFiles|check|uncheck|setChecked|pressSequentially)\s*\(|keyboard\.type\s*\(/;
|
|
5840
5999
|
ANY_CLICK = /\.click\s*\(/;
|
|
5841
6000
|
NAV_LOCATOR = /getByRole\s*\(\s*['"`](?:link|menuitem)['"`]/i;
|
|
5842
|
-
URL_PROOF = /\.toHaveURL\s*\(|\.waitForURL\s*\(/;
|
|
6001
|
+
URL_PROOF = /\.toHaveURL\s*\(|\.waitForURL\s*\(|\bexpect\s*\(\s*(?:await\s+)?page\s*\.\s*url\s*\(\s*\)\s*\)|\bexpect\s*\.\s*poll\s*\(\s*(?:async\s*)?\(\s*\)\s*=>\s*(?:\{\s*return\s+)?page\s*\.\s*url\s*\(\s*\)/;
|
|
5843
6002
|
RELOAD = /\.reload\s*\(/;
|
|
5844
6003
|
EXPECT_STMT = /\bexpect\s*[.(]/;
|
|
5845
6004
|
BLOCK_SCAFFOLD2 = /=>|\b(?:test|describe|it|beforeEach|afterEach|beforeAll|afterAll|function)\b/;
|
|
@@ -5868,6 +6027,7 @@ var init_assertion_strength = __esm({
|
|
|
5868
6027
|
INBOX_START = /^\s*\/\/\s*>>>\s*VALIDATE_QA_INBOX/;
|
|
5869
6028
|
INBOX_END = /^\s*\/\/\s*<<<\s*VALIDATE_QA_INBOX/;
|
|
5870
6029
|
NEGATED_ASSERT = /\.not\s*\.\s*to[A-Z]\w*|\.toBeHidden\s*\(/;
|
|
6030
|
+
NEGATED_ASSERT_NOT = /\.not\s*\.\s*to[A-Z]/;
|
|
5871
6031
|
CONST_CHAIN_MAX_DEPTH = 5;
|
|
5872
6032
|
TYPED_INPUT = /\.(fill|type|pressSequentially|setInputFiles)\s*\(/;
|
|
5873
6033
|
TITLE_LINE = /^\s*(?:test|describe)(?:\.\w+)?\s*\(/;
|
|
@@ -5883,8 +6043,14 @@ var init_assertion_strength = __esm({
|
|
|
5883
6043
|
CATCH_ALL_PATTERN = /^(['"`])\*\*(?:\/\*)?\1$|^\/\.\*\/[a-z]*$/;
|
|
5884
6044
|
GUARD_HANDLER = /\bMUTATING\.has\s*\(|blockedbyclient|Read-only guard/;
|
|
5885
6045
|
ABSENCE_MATCHER = /\.not\.toBeVisible\s*\(|\.toBeHidden\s*\(|\.toHaveCount\s*\(\s*0\s*\)/;
|
|
6046
|
+
VALUE_ABSENCE = /\.(?:count\s*\(\s*\)[\s)]{0,10}\.(?:(?:toBe|toEqual|toStrictEqual)\s*\(\s*0\s*\)|toBeLessThan\s*\(\s*1\s*\))|isVisible\s*\(\s*\)[\s)]{0,10}\.(?:(?:toBe|toEqual|toStrictEqual)\s*\(\s*false\s*\)|toBeFalsy\s*\()|isHidden\s*\(\s*\)[\s)]{0,10}\.(?:(?:toBe|toEqual|toStrictEqual)\s*\(\s*true\s*\)|toBeTruthy\s*\())/;
|
|
6047
|
+
OPTION_ABSENCE = /\.(?:toBeVisible\s*\(\s*\{[^{}()]{0,200}\bvisible|toBeAttached\s*\(\s*\{[^{}()]{0,200}\battached)\s*:\s*false\b/;
|
|
6048
|
+
NOT_BEFORE_MATCHER = /\.not\s*\.\s*(?=to[A-Z])/g;
|
|
5886
6049
|
ERROR_TEXT_WORDS = /error|fail|wrong|try again|unable|could ?n.?t|can.?t|problem|invalid|unavailable/i;
|
|
5887
6050
|
TEXT_REGEX_ARG = /(?:getByText|hasText|getByRole\([^)]{0,200}name)\s*[:(]\s*(\/(?:\\.|[^/\\\n])+\/[a-z]*)/;
|
|
6051
|
+
NEGATED_TEXT = /\.not\s*\.\s*(?:toHaveText|toContainText)\s*\(\s*(?:['"`]([^'"`]*)['"`]|\/((?:\\.|[^/\\\n])+)\/)?/;
|
|
6052
|
+
ASYNC_DESTRUCTURE_OPEN = /async\s*\(\s*\{/g;
|
|
6053
|
+
REQUEST_WORD = /\brequest\b/;
|
|
5888
6054
|
ROLE_NAME_RE = /getByRole\s*\(\s*['"`][a-z]+['"`]\s*,\s*\{[^}]*?name\s*:\s*(?:['"`]([^'"`]+)['"`]|\/([^/]+)\/)/gi;
|
|
5889
6055
|
INTERACT_VERB = /\.(click|fill|check|uncheck|setChecked|selectOption|pressSequentially)\s*\(/;
|
|
5890
6056
|
ROLE_SIG_RE = /getByRole\s*\(\s*['"`]([a-z]+)['"`]\s*,\s*\{[^}]*?name\s*:\s*(?:['"`]([^'"`]+)['"`]|\/([^/]+)\/)/gi;
|
|
@@ -5905,7 +6071,20 @@ var init_assertion_strength = __esm({
|
|
|
5905
6071
|
MATCHER_OPEN = /\s*(?:\.\s*(?:not|resolves|rejects)\s*)*\.\s*[A-Za-z_$][\w$]*\s*\(/y;
|
|
5906
6072
|
ENTERED_VALUE = /\.(?:fill|pressSequentially)\s*\(\s*['"`]([^'"`]+)['"`]|keyboard\.type\s*\(\s*['"`]([^'"`]+)['"`]|\.selectOption\s*\(\s*(?:\[\s*)?(?:\{\s*(?:label|value)\s*:\s*)?['"`]([^'"`]+)['"`]/g;
|
|
5907
6073
|
PAGE_NAV = /\.(?:goto|reload)\s*\(/;
|
|
6074
|
+
ENV_READ = String.raw`process\.env(?:(?:\?\.|\.)([A-Za-z_]\w*)|(?:\?\.)?\[\s*['"]([A-Za-z_]\w*)['"]\s*\])`;
|
|
6075
|
+
ENTERED_ENV = new RegExp(String.raw`\.(?:fill|pressSequentially)\s*\(\s*${ENV_READ}|keyboard\.type\s*\(\s*${ENV_READ}`, "g");
|
|
6076
|
+
HAVE_VALUE_ENV = new RegExp(String.raw`\.toHaveValue\s*\(\s*${ENV_READ}`);
|
|
5908
6077
|
IN_FLIGHT_TEXT = /^(?:(?:sending|saving|loading|searching|checking|processing|submitting|preparing|updating|please wait|working|one moment)\b[^.]{0,40}\.{2,3}|\.{3}|…)$/i;
|
|
6078
|
+
LOCATOR_VALUE_READ = /\.(?:count|textContent|innerText|innerHTML|inputValue|getAttribute|isVisible|isHidden|isChecked|isEnabled|isDisabled|isEditable|allTextContents|allInnerTexts|evaluate|evaluateAll|boundingBox|all)\s*\(/;
|
|
6079
|
+
LOCATOR_EXPR = /^(?:this\.)?page\s*\.\s*(?:getBy\w+|locator)\s*\(/;
|
|
6080
|
+
ALWAYS_TRUE_ON_OBJECT = /^\)\s*\.\s*(?:toBeTruthy|toBeDefined|not\s*\.\s*(?:toBeNull|toBeUndefined|toBeFalsy))\s*\(/;
|
|
6081
|
+
COUNT_AT_LEAST_ZERO = /\.(?:count\s*\(\s*\)|length)[\s)]{0,10}\.(?:toBeGreaterThanOrEqual\s*\(\s*0\s*\)|toBeGreaterThan\s*\(\s*-\s*1\s*\))/;
|
|
6082
|
+
DEFINED_VALUE_READ = /\.\s*(?:count|textContent|innerText|innerHTML|inputValue|getAttribute|title|url|isVisible|isHidden|isChecked|isEnabled|isDisabled|isEditable|allTextContents|allInnerTexts)\s*\([^()]*\)$/;
|
|
6083
|
+
ALWAYS_DEFINED = /^\)\s*\.\s*(?:toBeDefined|not\s*\.\s*toBeUndefined)\s*\(/;
|
|
6084
|
+
SELF_COMPARE_MATCHER = /^\)\s*\.\s*(toBe|toEqual|toStrictEqual|toHaveCount)\s*\(/;
|
|
6085
|
+
IS_VISIBLE_TRUE = /^\)*\s*\.\s*(?:toBe\s*\(\s*true\s*\)|toBeTruthy\s*\(\s*\))/;
|
|
6086
|
+
PAGE_VALUE_MATCHER = /^\)\s*\.\s*(?:toBe|toEqual|toStrictEqual|toMatch|toContain)\s*\(/;
|
|
6087
|
+
COUNT_ONE = /\.\s*toHaveCount\s*\(\s*1\s*\)/;
|
|
5909
6088
|
FORM_CONTROL_LOCATOR = /getByRole\s*\(\s*['"`](?:combobox|switch|checkbox|radio|textbox|searchbox|spinbutton|slider)['"`]|getByLabel\s*\(|getByPlaceholder\s*\(/;
|
|
5910
6089
|
CONTROL_VALUE_MATCHER = /\.(?:toHaveValue|toHaveText|toContainText)\s*\(|\.toHaveAttribute\s*\(\s*['"`]aria-checked['"`]/;
|
|
5911
6090
|
COPY_CONTROL = /\bcopy\b/i;
|
|
@@ -6114,7 +6293,7 @@ function reportedBugWhere() {
|
|
|
6114
6293
|
function bugReviewStatus(status) {
|
|
6115
6294
|
return status === "QUARANTINED" ? "confirmed" : "needs-review";
|
|
6116
6295
|
}
|
|
6117
|
-
var USER_CANCELLED_RUN_ERROR, HEAL_PATCH_HELD_RUN_ERROR_PREFIX, RUNNER_INTERRUPT_ERROR_PREFIX, SERVER_INTERRUPT_ERROR_PREFIX, RUN_LOST_ERROR_PREFIX, RUN_INTERRUPTED_ERROR_PREFIXES, RUN_INTERRUPTION_RETRIED_MARKER, RUN_INTERRUPTION_NOT_RETRIED_MARKERS, BUG_ASSERTION_TAG, UNEXPECTED_PASS_ERROR_PREFIX, BUG_TEST_BROKEN_ERROR_PREFIX, BUG_TEST_BROKEN_REASON, EMAIL_NOT_RECEIVED_PREFIXES, ENVIRONMENTAL_ERROR_PREFIXES, ENVIRONMENTAL_ERROR_STORED_PREFIXES, BUG_SURFACE_SUPPRESSING_TAGS, QUARANTINE_CLOSE_TAGS, BUG_CANDIDATE_TAG, BUG_TEST_BROKEN_TAG;
|
|
6296
|
+
var USER_CANCELLED_RUN_ERROR, HEAL_PATCH_HELD_RUN_ERROR_PREFIX, RUNNER_INTERRUPT_ERROR_PREFIX, SERVER_INTERRUPT_ERROR_PREFIX, RUN_LOST_ERROR_PREFIX, RUN_INTERRUPTED_ERROR_PREFIXES, RUN_INTERRUPTION_RETRIED_MARKER, RUN_INTERRUPTION_NOT_RETRIED_MARKERS, BUG_ASSERTION_TAG, UNEXPECTED_PASS_ERROR_PREFIX, BUG_TEST_BROKEN_ERROR_PREFIX, BUG_TEST_BROKEN_REASON, TEST_DID_NOT_RUN_ERROR, EMAIL_NOT_RECEIVED_PREFIXES, ENVIRONMENTAL_ERROR_PREFIXES, ENVIRONMENTAL_ERROR_STORED_PREFIXES, BUG_SURFACE_SUPPRESSING_TAGS, QUARANTINE_CLOSE_TAGS, BUG_CANDIDATE_TAG, BUG_TEST_BROKEN_TAG;
|
|
6118
6297
|
var init_run_classification = __esm({
|
|
6119
6298
|
"../shared/dist/run-classification.js"() {
|
|
6120
6299
|
"use strict";
|
|
@@ -6134,6 +6313,7 @@ var init_run_classification = __esm({
|
|
|
6134
6313
|
UNEXPECTED_PASS_ERROR_PREFIX = "UNEXPECTED_PASS:";
|
|
6135
6314
|
BUG_TEST_BROKEN_ERROR_PREFIX = "BUG_TEST_BROKEN:";
|
|
6136
6315
|
BUG_TEST_BROKEN_REASON = "bug test broken (not a bug verdict)";
|
|
6316
|
+
TEST_DID_NOT_RUN_ERROR = "Test did not run (a dependency failed or the run was interrupted)";
|
|
6137
6317
|
EMAIL_NOT_RECEIVED_PREFIXES = [
|
|
6138
6318
|
"VERIFICATION_EMAIL_NOT_RECEIVED:",
|
|
6139
6319
|
"VERIFICATION_EMAIL_HAS_NO_CODE:",
|
|
@@ -8875,6 +9055,23 @@ var init_auth_block_detector = __esm({
|
|
|
8875
9055
|
}
|
|
8876
9056
|
});
|
|
8877
9057
|
|
|
9058
|
+
// ../shared/dist/auth-session-stamp.js
|
|
9059
|
+
function parseAuthSessionStamp(value) {
|
|
9060
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
9061
|
+
return null;
|
|
9062
|
+
const { shared, role, generation } = value;
|
|
9063
|
+
if (shared !== true || role !== "producer" && role !== "follower")
|
|
9064
|
+
return null;
|
|
9065
|
+
return typeof generation === "string" && AUTH_SESSION_GENERATION_RE.test(generation) ? { shared: true, role, generation } : { shared: true, role };
|
|
9066
|
+
}
|
|
9067
|
+
var AUTH_SESSION_GENERATION_RE;
|
|
9068
|
+
var init_auth_session_stamp = __esm({
|
|
9069
|
+
"../shared/dist/auth-session-stamp.js"() {
|
|
9070
|
+
"use strict";
|
|
9071
|
+
AUTH_SESSION_GENERATION_RE = /^[0-9a-z]{1,16}$/;
|
|
9072
|
+
}
|
|
9073
|
+
});
|
|
9074
|
+
|
|
8878
9075
|
// ../shared/dist/auth-state-freshness.js
|
|
8879
9076
|
function isCsrfTokenName(name) {
|
|
8880
9077
|
return /csrf|xsrf|forgery|authenticity|requestverification/.test(name.toLowerCase());
|
|
@@ -10103,7 +10300,7 @@ var init_agentic_cli_runtime = __esm({
|
|
|
10103
10300
|
GROK_CLI_DEFAULT_MODEL = "grok-4.7";
|
|
10104
10301
|
GROK_CLI_DEFAULT_REASONING_EFFORT = "high";
|
|
10105
10302
|
CLAUDE_CLI_DEFAULT_MODEL = "claude-sonnet-5-5";
|
|
10106
|
-
CLAUDE_CLI_DEFAULT_EFFORT = "
|
|
10303
|
+
CLAUDE_CLI_DEFAULT_EFFORT = "high";
|
|
10107
10304
|
MUSE_CLI_DEFAULT_MODEL = "muse-spark-1.3-contributor";
|
|
10108
10305
|
MUSE_CLI_DEFAULT_EFFORT = "high";
|
|
10109
10306
|
CODEX_CLI_DEFAULT_MODEL = "gpt-6.1-sol";
|
|
@@ -10154,9 +10351,25 @@ function isOpenCodeGoModelId(id) {
|
|
|
10154
10351
|
function hasOpenCodeGoPrefix(id) {
|
|
10155
10352
|
return (id?.trim() ?? "").startsWith(OPENCODE_GO_PREFIX);
|
|
10156
10353
|
}
|
|
10354
|
+
function hasOpenCodeGo2Prefix(id) {
|
|
10355
|
+
return (id?.trim() ?? "").startsWith(OPENCODE_GO_2_PREFIX);
|
|
10356
|
+
}
|
|
10157
10357
|
function openCodeGoWireModel(id) {
|
|
10158
10358
|
const trimmed = id?.trim() ?? "";
|
|
10159
|
-
|
|
10359
|
+
if (!isOpenCodeGoModelId(trimmed))
|
|
10360
|
+
return null;
|
|
10361
|
+
return trimmed.slice((hasOpenCodeGo2Prefix(trimmed) ? OPENCODE_GO_2_PREFIX : OPENCODE_GO_PREFIX).length);
|
|
10362
|
+
}
|
|
10363
|
+
function isZaiModelId(id) {
|
|
10364
|
+
const trimmed = id?.trim() ?? "";
|
|
10365
|
+
return ZAI_MODEL_PATTERN.test(trimmed) && !trimmed.includes("..");
|
|
10366
|
+
}
|
|
10367
|
+
function hasZaiPrefix(id) {
|
|
10368
|
+
return (id?.trim() ?? "").startsWith(ZAI_PREFIX);
|
|
10369
|
+
}
|
|
10370
|
+
function zaiWireModel(id) {
|
|
10371
|
+
const trimmed = id?.trim() ?? "";
|
|
10372
|
+
return isZaiModelId(trimmed) ? trimmed.slice(ZAI_PREFIX.length) : null;
|
|
10160
10373
|
}
|
|
10161
10374
|
function isEngineId(value) {
|
|
10162
10375
|
return CLI_ENGINE_MODEL_IDS.includes(value);
|
|
@@ -10254,7 +10467,7 @@ function museCliContributorMode(env = process.env) {
|
|
|
10254
10467
|
const raw = (env.MUSE_CLI_CONTRIBUTOR_MODE ?? "").trim().toLowerCase();
|
|
10255
10468
|
return raw === "off" || raw === "false" || raw === "0" || raw === "no" ? "off" : "on";
|
|
10256
10469
|
}
|
|
10257
|
-
var CLI_ENGINE_MODEL_IDS, BROWSER_CLI_ENGINE_IDS, PIPELINE_STEPS, CLI_ENGINE_RULES, ENGINE_NAMES, OPENROUTER_PREFIX, OPENROUTER_MODEL_PATTERN, OPENCODE_GO_PREFIX, OPENCODE_GO_MODEL_PATTERN;
|
|
10470
|
+
var CLI_ENGINE_MODEL_IDS, BROWSER_CLI_ENGINE_IDS, PIPELINE_STEPS, CLI_ENGINE_RULES, ENGINE_NAMES, OPENROUTER_PREFIX, OPENROUTER_MODEL_PATTERN, OPENCODE_GO_PREFIX, OPENCODE_GO_2_PREFIX, OPENCODE_GO_MODEL_PATTERN, ZAI_PREFIX, ZAI_MODEL_PATTERN;
|
|
10258
10471
|
var init_engine_spec = __esm({
|
|
10259
10472
|
"../shared/dist/engine-spec.js"() {
|
|
10260
10473
|
"use strict";
|
|
@@ -10313,7 +10526,10 @@ var init_engine_spec = __esm({
|
|
|
10313
10526
|
OPENROUTER_PREFIX = "openrouter:";
|
|
10314
10527
|
OPENROUTER_MODEL_PATTERN = /^openrouter:[a-z0-9][a-z0-9._-]{0,40}\/[A-Za-z0-9._:-]{1,100}$/;
|
|
10315
10528
|
OPENCODE_GO_PREFIX = "opencode-go:";
|
|
10316
|
-
|
|
10529
|
+
OPENCODE_GO_2_PREFIX = "opencode-go-2:";
|
|
10530
|
+
OPENCODE_GO_MODEL_PATTERN = /^opencode-go(?:-2)?:[a-z0-9][a-z0-9._-]{0,100}$/;
|
|
10531
|
+
ZAI_PREFIX = "zai:";
|
|
10532
|
+
ZAI_MODEL_PATTERN = /^zai:glm-[a-z0-9][a-z0-9._-]{0,60}$/;
|
|
10317
10533
|
}
|
|
10318
10534
|
});
|
|
10319
10535
|
|
|
@@ -10598,6 +10814,8 @@ var init_api_model_catalog = __esm({
|
|
|
10598
10814
|
authCaptureModel: "Login capture",
|
|
10599
10815
|
plannerModel: "Scenario Planner",
|
|
10600
10816
|
plannerGateModel: "Planner Gate",
|
|
10817
|
+
pwAgentsHealerModel: "Test Healer (Playwright agents)",
|
|
10818
|
+
pwAgentsReviewModel: "Plan Review (Playwright agents)",
|
|
10601
10819
|
generationModel: "Code Generation",
|
|
10602
10820
|
reviewModel: "Code Review",
|
|
10603
10821
|
apiTestModel: "API Test Generator",
|
|
@@ -10622,11 +10840,6 @@ var init_api_model_catalog = __esm({
|
|
|
10622
10840
|
});
|
|
10623
10841
|
|
|
10624
10842
|
// ../shared/dist/provider-chains.js
|
|
10625
|
-
function isClaudeModel(id) {
|
|
10626
|
-
if (/claude|anthropic/i.test(id))
|
|
10627
|
-
return true;
|
|
10628
|
-
return API_MODEL_CATALOG.some((m) => m.provider === "anthropic" && (m.id === id || m.apiModel === id));
|
|
10629
|
-
}
|
|
10630
10843
|
function chainKey(modelId) {
|
|
10631
10844
|
const id = canonicalModelId(modelId);
|
|
10632
10845
|
if (!id)
|
|
@@ -10634,34 +10847,83 @@ function chainKey(modelId) {
|
|
|
10634
10847
|
const entry = API_MODEL_CATALOG.find((m) => m.id === id || m.apiModel === id);
|
|
10635
10848
|
return entry?.id ?? id;
|
|
10636
10849
|
}
|
|
10850
|
+
function has(map, key) {
|
|
10851
|
+
return Object.prototype.hasOwnProperty.call(map, key);
|
|
10852
|
+
}
|
|
10637
10853
|
function providerChainFor(modelId) {
|
|
10638
10854
|
const key = chainKey(modelId);
|
|
10639
|
-
|
|
10640
|
-
|
|
10641
|
-
|
|
10855
|
+
return key && has(PROVIDER_CHAINS, key) ? PROVIDER_CHAINS[key] : [];
|
|
10856
|
+
}
|
|
10857
|
+
function providerChainLeadsFor(modelId) {
|
|
10858
|
+
const key = chainKey(modelId);
|
|
10859
|
+
return key && has(PROVIDER_CHAIN_LEADS, key) ? PROVIDER_CHAIN_LEADS[key] : [];
|
|
10860
|
+
}
|
|
10861
|
+
function allChains() {
|
|
10862
|
+
return [...Object.entries(PROVIDER_CHAINS), ...Object.entries(PROVIDER_CHAIN_LEADS)];
|
|
10642
10863
|
}
|
|
10643
10864
|
function isProviderChainHopId(modelId) {
|
|
10644
10865
|
const id = modelId ?? "";
|
|
10645
|
-
return id !== "" &&
|
|
10866
|
+
return id !== "" && allChains().some(([, hops]) => hops.includes(id));
|
|
10646
10867
|
}
|
|
10647
10868
|
function providerChainPrimariesForHop(hopId) {
|
|
10648
10869
|
const id = hopId ?? "";
|
|
10649
10870
|
if (!id)
|
|
10650
10871
|
return [];
|
|
10651
|
-
return
|
|
10872
|
+
return allChains().filter(([, hops]) => hops.includes(id)).map(([primary]) => primary);
|
|
10873
|
+
}
|
|
10874
|
+
function providerChainHostOf(value, modelValue) {
|
|
10875
|
+
if (value === modelValue)
|
|
10876
|
+
return "own";
|
|
10877
|
+
if (hasZaiPrefix(value))
|
|
10878
|
+
return "zai";
|
|
10879
|
+
if (hasOpenCodeGo2Prefix(value))
|
|
10880
|
+
return "opencode-go-2";
|
|
10881
|
+
if (hasOpenCodeGoPrefix(value))
|
|
10882
|
+
return "opencode-go";
|
|
10883
|
+
if (hasOpenRouterPrefix(value))
|
|
10884
|
+
return "openrouter";
|
|
10885
|
+
return "own";
|
|
10886
|
+
}
|
|
10887
|
+
function providerHostPlan(modelId) {
|
|
10888
|
+
const key = chainKey(modelId);
|
|
10889
|
+
if (!key)
|
|
10890
|
+
return [];
|
|
10891
|
+
const own = { host: hasOpenRouterPrefix(key) ? "openrouter" : "own", value: key };
|
|
10892
|
+
const steps = [
|
|
10893
|
+
...providerChainLeadsFor(key).map((value) => ({ host: providerChainHostOf(value, key), value })),
|
|
10894
|
+
own,
|
|
10895
|
+
...providerChainFor(key).map((value) => ({ host: providerChainHostOf(value, key), value }))
|
|
10896
|
+
];
|
|
10897
|
+
const notOffered = Object.prototype.hasOwnProperty.call(PROVIDER_HOST_NOT_OFFERED, key) ? PROVIDER_HOST_NOT_OFFERED[key] : {};
|
|
10898
|
+
for (const [host, reason] of Object.entries(notOffered)) {
|
|
10899
|
+
const at = steps.findIndex((step) => step === own) + 1;
|
|
10900
|
+
steps.splice(at, 0, { host, value: null, notOffered: reason });
|
|
10901
|
+
}
|
|
10902
|
+
return steps;
|
|
10652
10903
|
}
|
|
10653
|
-
var PROVIDER_CHAINS;
|
|
10904
|
+
var PROVIDER_CHAINS, PROVIDER_CHAIN_LEADS, PROVIDER_HOST_NOT_OFFERED;
|
|
10654
10905
|
var init_provider_chains = __esm({
|
|
10655
10906
|
"../shared/dist/provider-chains.js"() {
|
|
10656
10907
|
"use strict";
|
|
10657
10908
|
init_api_model_catalog();
|
|
10909
|
+
init_engine_spec();
|
|
10658
10910
|
PROVIDER_CHAINS = {
|
|
10659
|
-
"minimax-m3": ["
|
|
10660
|
-
"minimax-m3.1-flash": ["
|
|
10911
|
+
"minimax-m3": ["opencode-go:minimax-m3", "openrouter:minimax/minimax-m3"],
|
|
10912
|
+
"minimax-m3.1-flash": ["opencode-go:minimax-m3.1-flash", "openrouter:minimax/minimax-m3.1-flash"],
|
|
10661
10913
|
"muse-spark-1.3-contributor": ["openrouter:meta/muse-spark-1.3-contributor"],
|
|
10662
|
-
"
|
|
10663
|
-
"
|
|
10664
|
-
"
|
|
10914
|
+
"claude-haiku-5-5": ["opencode-go:claude-haiku-5-5", "openrouter:anthropic/claude-haiku-5.5"],
|
|
10915
|
+
"gemini-3.1-pro-preview": ["openrouter:google/gemini-3.1-pro-preview"],
|
|
10916
|
+
"deepseek-flash": ["opencode-go-2:deepseek-v4.1-flash", "opencode-go:deepseek-v4.1-flash", "openrouter:deepseek/deepseek-v4.1-flash"],
|
|
10917
|
+
"deepseek-v4-pro": ["opencode-go-2:deepseek-v4-pro", "opencode-go:deepseek-v4-pro", "openrouter:deepseek/deepseek-v4-pro"]
|
|
10918
|
+
};
|
|
10919
|
+
PROVIDER_CHAIN_LEADS = {
|
|
10920
|
+
"openrouter:z-ai/glm-5.3-flash": ["zai:glm-5.3-flash", "opencode-go-2:glm-5.3-flash", "opencode-go:glm-5.3-flash"],
|
|
10921
|
+
"openrouter:z-ai/glm-5.3": ["zai:glm-5.3", "opencode-go-2:glm-5.3", "opencode-go:glm-5.3"],
|
|
10922
|
+
"openrouter:z-ai/glm-5.2": ["zai:glm-5.2", "opencode-go-2:glm-5.2", "opencode-go:glm-5.2"]
|
|
10923
|
+
};
|
|
10924
|
+
PROVIDER_HOST_NOT_OFFERED = {
|
|
10925
|
+
"muse-spark-1.3-contributor": { "opencode-go": "OpenCode Go serves Muse only on its Responses API" },
|
|
10926
|
+
"gemini-3.1-pro-preview": { "opencode-go": "OpenCode Go does not serve Gemini" }
|
|
10665
10927
|
};
|
|
10666
10928
|
}
|
|
10667
10929
|
});
|
|
@@ -12620,7 +12882,7 @@ function stateLiteralsOf(line) {
|
|
|
12620
12882
|
const out = [];
|
|
12621
12883
|
for (const m of line.matchAll(STRING_LITERAL_RE)) {
|
|
12622
12884
|
const text = m[1] ?? m[2] ?? m[3] ?? "";
|
|
12623
|
-
if (
|
|
12885
|
+
if (PLACEHOLDER_ATTR_RE.test(text))
|
|
12624
12886
|
continue;
|
|
12625
12887
|
if (PLACEHOLDER_CALL_TAIL_RE.test(line.slice(Math.max(0, m.index - 40), m.index)))
|
|
12626
12888
|
continue;
|
|
@@ -12716,7 +12978,7 @@ function formatReadinessPollAdvisory(findings) {
|
|
|
12716
12978
|
const where = findings.length === 1 ? "One place" : `${findings.length} places`;
|
|
12717
12979
|
return `${where} re-check a background state once and assert the finished state at once (${short(first.line, 90)} then ${short(first.assertion, 90)}). ${first.reason}`;
|
|
12718
12980
|
}
|
|
12719
|
-
var READINESS_POLL_MAX_CODE_CHARS, MAX_LINE_CHARS, MAX_FINDINGS, ASSERTION_WINDOW_LINES, MAX_BLOCK_CHARS, MAX_HEAD_CHARS, STRING_LITERAL_RE, RECHECK_NAME_RE, RETRY_NAME_RE, PENDING_STATE_RE, LOCATOR_LINE_RE, CLICK_RE, RELOAD_RE, GOTO_RE, ACTION_RE, EXPECT_RE, NEGATED_RE, PENDING_ENDED_RE, POSITIVE_MATCHER_RE, ZERO_COUNT_RE, TEST_END_RE, TEST_START_RE, POLL_OPEN_RE, PLACEHOLDER_CALL_TAIL_RE, POLL_SHAPE;
|
|
12981
|
+
var READINESS_POLL_MAX_CODE_CHARS, MAX_LINE_CHARS, MAX_FINDINGS, ASSERTION_WINDOW_LINES, MAX_BLOCK_CHARS, MAX_HEAD_CHARS, STRING_LITERAL_RE, RECHECK_NAME_RE, RETRY_NAME_RE, PENDING_STATE_RE, LOCATOR_LINE_RE, CLICK_RE, RELOAD_RE, GOTO_RE, ACTION_RE, EXPECT_RE, NEGATED_RE, PENDING_ENDED_RE, POSITIVE_MATCHER_RE, ZERO_COUNT_RE, TEST_END_RE, TEST_START_RE, POLL_OPEN_RE, PLACEHOLDER_ATTR_RE, PLACEHOLDER_CALL_TAIL_RE, POLL_SHAPE;
|
|
12720
12982
|
var init_readiness_poll = __esm({
|
|
12721
12983
|
"../shared/dist/readiness-poll.js"() {
|
|
12722
12984
|
"use strict";
|
|
@@ -12729,7 +12991,7 @@ var init_readiness_poll = __esm({
|
|
|
12729
12991
|
STRING_LITERAL_RE = /'((?:\\.|[^'\\\n]){0,300})'|"((?:\\.|[^"\\\n]){0,300})"|`((?:\\.|[^`\\\n]){0,300})`/g;
|
|
12730
12992
|
RECHECK_NAME_RE = /^\s{0,10}(?:check again|check (?:status|availability|now|for updates|progress)|re-?check(?: status)?|update status|see (?:latest|updates))\s{0,10}(?:\.\.\.|…)?\s{0,10}$/i;
|
|
12731
12993
|
RETRY_NAME_RE = /^\s{0,10}(?:refresh(?: (?:status|list|page|results|now))?|reload(?: (?:page|results|list))?|try again|retry|load (?:again|latest))\s{0,10}(?:\.\.\.|…)?\s{0,10}$/i;
|
|
12732
|
-
PENDING_STATE_RE = /\b(?:searching|checking|sending|pending|processing|queued|in progress|loading|generating|calculating|preparing|verifying|syncing|refreshing|not ready|please wait|waiting)\b|\b[a-z]{3,
|
|
12994
|
+
PENDING_STATE_RE = /\b(?:searching|checking|sending|pending|processing|queued|in progress|loading|generating|calculating|preparing|verifying|syncing|refreshing|not ready|please wait|waiting)\b|\b(?:saving|uploading|downloading|signing|logging|submitting|creating|updating|deleting|importing|exporting|publishing|connecting|redirecting|authenticating|installing|retrieving|fetching|analy[sz]ing|running|rendering|restarting|building|compiling|booting|provisioning|deploying|indexing|migrating|launching|initiali[sz]ing|optimi[sz]ing|converting|processing)(?:\s{1,3}[a-z]{1,20}){0,2}\s{0,3}(?:\.\.\.|…)\s{0,10}$|^\s{0,10}(?:starting|scanning|working|applying|finishing)(?:\s{1,3}[a-z]{1,20}){0,2}\s{0,3}(?:\.\.\.|…)\s{0,10}$|^\s{0,10}[a-z]{3,40}(?<!th)ing\s{0,3}(?:\.\.\.|…)\s{0,10}$/i;
|
|
12733
12995
|
LOCATOR_LINE_RE = /\.(?:getBy[A-Z]\w{0,30}|locator|toHaveText|toContainText)\s{0,20}\(/;
|
|
12734
12996
|
CLICK_RE = /\.(?:click|dblclick|tap)\s{0,20}\(/;
|
|
12735
12997
|
RELOAD_RE = /\bpage\.reload\s{0,20}\(/;
|
|
@@ -12743,6 +13005,7 @@ var init_readiness_poll = __esm({
|
|
|
12743
13005
|
TEST_END_RE = /^\s{0,20}\}\s{0,20}\)\s{0,20};?\s{0,20}$/;
|
|
12744
13006
|
TEST_START_RE = /\btest(?:\.\w{1,20})?\s{0,20}\(/;
|
|
12745
13007
|
POLL_OPEN_RE = /\bexpect(?:\.soft)?\s{0,20}\(\s{0,20}async\b|\bexpect\.poll\s{0,20}\(|\bfor\s{0,20}\(|\bwhile\s{0,20}\(|\bdo\s{0,20}\{/g;
|
|
13008
|
+
PLACEHOLDER_ATTR_RE = /placeholder\s{0,4}[*^$~|]?=/;
|
|
12746
13009
|
PLACEHOLDER_CALL_TAIL_RE = /getByPlaceholder\s{0,20}\(\s{0,20}$/;
|
|
12747
13010
|
POLL_SHAPE = "await expect(async () => { await page.reload(); await expect(<the finished state>).toBeVisible({ timeout: 1500 }); }).toPass({ timeout: 20000 });";
|
|
12748
13011
|
}
|
|
@@ -13144,6 +13407,15 @@ function computes(line) {
|
|
|
13144
13407
|
const code = line.replace(STRING_RE, '""').replace(REGEX_LITERAL_RE, "$1RX").replace(LITERAL_ARITH_RE, "N");
|
|
13145
13408
|
return COMPUTE_RE.test(code) || RELATIONAL_RE.test(line);
|
|
13146
13409
|
}
|
|
13410
|
+
function identifiersIn(line) {
|
|
13411
|
+
const code = line.replace(STRING_RE, '""').replace(REGEX_LITERAL_RE, "$1RX");
|
|
13412
|
+
return [code, ...[...line.matchAll(TEMPLATE_EXPR_RE)].map((m) => m[1])].flatMap((part) => part.match(IDENTIFIER_RE) ?? []);
|
|
13413
|
+
}
|
|
13414
|
+
function addTyped(typed, value) {
|
|
13415
|
+
const d = digitsOf(value);
|
|
13416
|
+
if (/\d/.test(d))
|
|
13417
|
+
typed.add(d);
|
|
13418
|
+
}
|
|
13147
13419
|
function reasonFor4(kind, label, value) {
|
|
13148
13420
|
const what = kind === "derived-value-shape-only" ? `The ${label} is checked only for its shape, so any amount passes, including a wrongly computed one.` : `The ${label} is checked against a number written into the test (${value}). That number was read off the page, so when the page computes it wrong the check still passes.`;
|
|
13149
13421
|
return `${what} Instead, ${COMPUTE_SHAPE} If the number is not computed from other values (a list price, an id, a fixed fee), leave it as it is. Keep the existing checks.`;
|
|
@@ -13155,25 +13427,32 @@ function analyzeDerivedValues(rawCode) {
|
|
|
13155
13427
|
for (const body of testBodies(rawCode)) {
|
|
13156
13428
|
if (findings.length >= MAX_FINDINGS4)
|
|
13157
13429
|
break;
|
|
13158
|
-
if (body.some(computes))
|
|
13159
|
-
continue;
|
|
13160
13430
|
const typed = /* @__PURE__ */ new Set();
|
|
13161
13431
|
const definitions = /* @__PURE__ */ new Map();
|
|
13432
|
+
const computed = /* @__PURE__ */ new Set();
|
|
13433
|
+
const usesComputed = (text) => identifiersIn(text).some((id) => computed.has(id));
|
|
13162
13434
|
for (const line of body) {
|
|
13163
13435
|
if (FILL_RE2.test(line) && !EXPECT_RE3.test(line)) {
|
|
13164
|
-
for (const m of line.matchAll(
|
|
13165
|
-
|
|
13166
|
-
|
|
13167
|
-
|
|
13168
|
-
|
|
13436
|
+
for (const m of line.matchAll(QUOTED_RE))
|
|
13437
|
+
addTyped(typed, m[1]);
|
|
13438
|
+
const arg = FILL_ARG_RE.exec(line)?.[1];
|
|
13439
|
+
const literal = arg === void 0 ? null : LITERAL_VALUE_RE.exec(definitions.get(arg) ?? "");
|
|
13440
|
+
const value = literal?.slice(1).find((v) => v !== void 0);
|
|
13441
|
+
if (value !== void 0)
|
|
13442
|
+
addTyped(typed, value);
|
|
13169
13443
|
}
|
|
13170
13444
|
const def = DEFINITION_RE.exec(line);
|
|
13171
|
-
if (def)
|
|
13445
|
+
if (def) {
|
|
13172
13446
|
definitions.set(def[1], def[2]);
|
|
13447
|
+
if (computes(def[2]) || usesComputed(def[2]))
|
|
13448
|
+
computed.add(def[1]);
|
|
13449
|
+
}
|
|
13173
13450
|
}
|
|
13174
13451
|
for (const line of body) {
|
|
13175
13452
|
if (!EXPECT_RE3.test(line) || !VALUE_MATCHER_RE2.test(line) || /\.not\s{0,20}\./.test(line))
|
|
13176
13453
|
continue;
|
|
13454
|
+
if (computes(line) || usesComputed(line))
|
|
13455
|
+
continue;
|
|
13177
13456
|
const subject = EXPECT_SUBJECT_RE.exec(line)?.[1];
|
|
13178
13457
|
const label = DERIVED_WORD_RE.exec(`${line} ${(subject && definitions.get(subject)) ?? ""}`)?.[0];
|
|
13179
13458
|
if (!label)
|
|
@@ -13203,7 +13482,7 @@ function formatDerivedValueAdvisory(findings) {
|
|
|
13203
13482
|
const where = findings.length === 1 ? "One computed value is" : `${findings.length} computed values are`;
|
|
13204
13483
|
return `${where} checked against a copied number or only its shape (${short4(first.line, 90)}). ${first.reason}`;
|
|
13205
13484
|
}
|
|
13206
|
-
var MAX_FINDINGS4, EXPECT_RE3, DERIVED_WORD_RE, VALUE_MATCHER_RE2, MONEY_TOKEN_RE, SHAPE_ARG_RE, FILL_RE2, EXPECT_MESSAGE_RE, STRING_RE, REGEX_LITERAL_RE, COMPUTE_RE, RELATIONAL_RE, DEFINITION_RE, EXPECT_SUBJECT_RE, LITERAL_ARITH_RE, COMPUTE_SHAPE;
|
|
13485
|
+
var MAX_FINDINGS4, EXPECT_RE3, DERIVED_WORD_RE, VALUE_MATCHER_RE2, MONEY_TOKEN_RE, SHAPE_ARG_RE, FILL_RE2, EXPECT_MESSAGE_RE, STRING_RE, REGEX_LITERAL_RE, COMPUTE_RE, RELATIONAL_RE, DEFINITION_RE, EXPECT_SUBJECT_RE, LITERAL_ARITH_RE, TEMPLATE_EXPR_RE, IDENTIFIER_RE, QUOTED_RE, FILL_ARG_RE, LITERAL_VALUE_RE, COMPUTE_SHAPE;
|
|
13207
13486
|
var init_derived_value_check = __esm({
|
|
13208
13487
|
"../shared/dist/derived-value-check.js"() {
|
|
13209
13488
|
"use strict";
|
|
@@ -13223,6 +13502,11 @@ var init_derived_value_check = __esm({
|
|
|
13223
13502
|
DEFINITION_RE = /\b(?:const|let)\s{1,10}([\w$]{1,60})\s{0,10}=\s{0,10}([^\n]{0,300})/;
|
|
13224
13503
|
EXPECT_SUBJECT_RE = /\bexpect(?:\.soft)?\s{0,20}\(\s{0,20}([\w$]{1,60})/;
|
|
13225
13504
|
LITERAL_ARITH_RE = /\b\d[\d_.]{0,15}(?:\s{0,5}[*/+-]\s{0,5}\d[\d_.]{0,15}){1,8}\b/g;
|
|
13505
|
+
TEMPLATE_EXPR_RE = /\$\{([^{}\n]{0,200})\}/g;
|
|
13506
|
+
IDENTIFIER_RE = /[A-Za-z_$][\w$]{0,60}/g;
|
|
13507
|
+
QUOTED_RE = /['"`]([^'"`\n]{0,40})['"`]/g;
|
|
13508
|
+
FILL_ARG_RE = /\.(?:fill|type|pressSequentially|selectOption)\s{0,20}\(\s{0,20}([A-Za-z_$][\w$]{0,60})\s{0,20}[,)]/;
|
|
13509
|
+
LITERAL_VALUE_RE = /^\s{0,10}(?:'([^'\n]{0,40})'|"([^"\n]{0,40})"|`([^`$\n]{0,40})`|(\d[\d_,]{0,15}(?:\.\d{1,4})?))\s{0,10};?\s{0,10}$/;
|
|
13226
13510
|
COMPUTE_SHAPE = "compute the expected value in the test from its inputs (the values the test typed, or each input read from the page) and compare: const toNumber = (s) => Number(s.replace(/[^\\d.-]/g, '')); const unit = toNumber(await <unit price locator>.innerText()); then expect(toNumber(await <shown value locator>.innerText())).toBeCloseTo(unit * quantity, 2); for several lines, read them all and sum them with reduce. For a comma-decimal price like \u20AC1.234,56, drop the dots and turn the comma into a point before Number, since the replace above would read it as 1.23456.";
|
|
13227
13511
|
}
|
|
13228
13512
|
});
|
|
@@ -15257,6 +15541,99 @@ function formatStaleAfterSubmitAdvisory(rawCode) {
|
|
|
15257
15541
|
const more = described.length > 3 ? ` (+${described.length - 3} more)` : "";
|
|
15258
15542
|
return `stale-after-submit: ${described.slice(0, 3).join("; ")}${more}; these pass before the request lands and still pass if the server rejects or ignores the save. Wait for the result instead: page.waitForResponse around the click, or assert something the submit creates (the new row, the confirmation message, a path with an end anchor such as /\\/orders\\/\\d+$/)`;
|
|
15259
15543
|
}
|
|
15544
|
+
function regexLiteralEnd4(text, at) {
|
|
15545
|
+
let inClass = false;
|
|
15546
|
+
for (let i = at + 1; i < text.length; i++) {
|
|
15547
|
+
const ch = text[i];
|
|
15548
|
+
if (ch === "\n")
|
|
15549
|
+
return -1;
|
|
15550
|
+
if (ch === "\\") {
|
|
15551
|
+
i++;
|
|
15552
|
+
continue;
|
|
15553
|
+
}
|
|
15554
|
+
if (inClass) {
|
|
15555
|
+
if (ch === "]")
|
|
15556
|
+
inClass = false;
|
|
15557
|
+
continue;
|
|
15558
|
+
}
|
|
15559
|
+
if (ch === "[") {
|
|
15560
|
+
inClass = true;
|
|
15561
|
+
continue;
|
|
15562
|
+
}
|
|
15563
|
+
if (ch === "/")
|
|
15564
|
+
return i + 1;
|
|
15565
|
+
}
|
|
15566
|
+
return -1;
|
|
15567
|
+
}
|
|
15568
|
+
function startsWithRegexLiteral(text) {
|
|
15569
|
+
const t = text.trimStart();
|
|
15570
|
+
return t[0] === "/" && t[1] !== "/" && t[1] !== "*" && regexLiteralEnd4(t, 0) !== -1;
|
|
15571
|
+
}
|
|
15572
|
+
function afterFirstArg(args) {
|
|
15573
|
+
let depth = 0;
|
|
15574
|
+
let quote = null;
|
|
15575
|
+
for (let i = 0; i < args.length; i++) {
|
|
15576
|
+
const ch = args[i];
|
|
15577
|
+
if (quote) {
|
|
15578
|
+
if (ch === "\\") {
|
|
15579
|
+
i++;
|
|
15580
|
+
continue;
|
|
15581
|
+
}
|
|
15582
|
+
if (ch === quote)
|
|
15583
|
+
quote = null;
|
|
15584
|
+
continue;
|
|
15585
|
+
}
|
|
15586
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
15587
|
+
quote = ch;
|
|
15588
|
+
continue;
|
|
15589
|
+
}
|
|
15590
|
+
if (ch === "(" || ch === "[" || ch === "{")
|
|
15591
|
+
depth++;
|
|
15592
|
+
else if (ch === ")" || ch === "]" || ch === "}")
|
|
15593
|
+
depth--;
|
|
15594
|
+
else if (ch === "," && depth === 0)
|
|
15595
|
+
return args.slice(i + 1);
|
|
15596
|
+
}
|
|
15597
|
+
return "";
|
|
15598
|
+
}
|
|
15599
|
+
function analyzeRegexMatchers(rawCode) {
|
|
15600
|
+
if (!rawCode || typeof rawCode !== "string")
|
|
15601
|
+
return [];
|
|
15602
|
+
const code = stripComments2(rawCode.slice(0, MAX_LINT_INPUT));
|
|
15603
|
+
const out = [];
|
|
15604
|
+
const re = /\bexpect(?:\.soft)?\s{0,20}\(/g;
|
|
15605
|
+
let m;
|
|
15606
|
+
let examined = 0;
|
|
15607
|
+
let line = 1;
|
|
15608
|
+
let lineAt2 = 0;
|
|
15609
|
+
while ((m = re.exec(code)) !== null && examined++ < MAX_CALLS && out.length < MAX_REGEX_MATCHER_FINDINGS) {
|
|
15610
|
+
const subj = readBalanced(code, m.index + m[0].length);
|
|
15611
|
+
if (!subj)
|
|
15612
|
+
continue;
|
|
15613
|
+
const mm = /^\s{0,20}(\.\s{0,20}not)?\s{0,20}\.\s{0,20}(\w{1,40})\s{0,20}\(/.exec(code.slice(subj.end + 1, subj.end + 1 + 80));
|
|
15614
|
+
if (!mm || !REGEX_EQUALITY_MATCHERS.has(mm[2]))
|
|
15615
|
+
continue;
|
|
15616
|
+
const args = readBalanced(code, subj.end + 1 + mm[0].length);
|
|
15617
|
+
if (!args)
|
|
15618
|
+
continue;
|
|
15619
|
+
const expected = mm[2] === "toHaveJSProperty" ? afterFirstArg(args.text) : args.text;
|
|
15620
|
+
if (!startsWithRegexLiteral(expected))
|
|
15621
|
+
continue;
|
|
15622
|
+
if (mm[2] !== "toHaveJSProperty" && mm[2] !== "toContain" && (startsWithRegexLiteral(subj.text) || /^new\s+RegExp\b/.test(subj.text.trim())))
|
|
15623
|
+
continue;
|
|
15624
|
+
for (let i = code.indexOf("\n", lineAt2); i !== -1 && i < m.index; i = code.indexOf("\n", i + 1)) {
|
|
15625
|
+
line++;
|
|
15626
|
+
lineAt2 = i + 1;
|
|
15627
|
+
}
|
|
15628
|
+
out.push({ line, matcher: mm[2], negated: mm[1] !== void 0 });
|
|
15629
|
+
}
|
|
15630
|
+
return out;
|
|
15631
|
+
}
|
|
15632
|
+
function formatRegexMatcherNote(findings) {
|
|
15633
|
+
const lines = findings.map((f) => `Line ${f.line}: .${f.negated ? "not." : ""}${f.matcher}(\u2026 /regex/ \u2026) ${f.negated ? "always passes (a regex never equals the value), so it checks nothing" : "can never pass: this matcher compares by equality and a regex never equals a value"}.`);
|
|
15634
|
+
return `${lines.join("\n")}
|
|
15635
|
+
A regex only works in toMatch, toHaveText, toContainText, toHaveValue and similar text matchers. For a DOM property read it first: expect(await loc.evaluate((n) => n.<prop>)).toMatch(/re/). Or give toHaveJSProperty the exact string (or boolean) the page shows. Keep every other line as-is.`;
|
|
15636
|
+
}
|
|
15260
15637
|
function lintTestQuality(rawCode, opts = {}) {
|
|
15261
15638
|
if (!rawCode || typeof rawCode !== "string")
|
|
15262
15639
|
return [];
|
|
@@ -15264,6 +15641,10 @@ function lintTestQuality(rawCode, opts = {}) {
|
|
|
15264
15641
|
return lintTestData(rawCode, { pageContacts: opts.pageContacts });
|
|
15265
15642
|
if (opts.only === "test-data-blocking")
|
|
15266
15643
|
return analyzeTestData(rawCode, { pageContacts: opts.pageContacts }).blocking;
|
|
15644
|
+
if (opts.only === "regex-matcher") {
|
|
15645
|
+
const found = analyzeRegexMatchers(rawCode);
|
|
15646
|
+
return found.length > 0 ? [formatRegexMatcherNote(found)] : [];
|
|
15647
|
+
}
|
|
15267
15648
|
const warnings = [];
|
|
15268
15649
|
if (rawCode.length > MAX_LINT_INPUT) {
|
|
15269
15650
|
warnings.push(`The code is ${Math.round(rawCode.length / 1024)} KB; the quality lint checked only the first ${MAX_LINT_INPUT / 1e3} KB. Split it into smaller tests (one behaviour per test).`);
|
|
@@ -15538,7 +15919,7 @@ function specItemsFromTestPlans(testPlans) {
|
|
|
15538
15919
|
}
|
|
15539
15920
|
return [...new Set(out)].slice(0, 200);
|
|
15540
15921
|
}
|
|
15541
|
-
var READ_ONLY_TAGS, PRODUCTION_READ_ONLY_GUARD_TEMPLATE, HOST_AGNOSTIC_URL_TEMPLATE, TEST_QUALITY_CONVENTIONS, NON_PROD_HOST_LABEL, NON_PROD_HOST_TOKEN, TRANSIENT_TEXT, STRING_LITERAL, FIRST_LITERAL, GET_BY_TEXT_LITERAL2, ROLE_NAME_LITERAL2, MAX_LINT_INPUT, MAX_BALANCED_SCAN, MAX_CALLS, MAX_SEEN_SCAN, ACTION_CALL, BY_NAME_LOCATOR, ACTION_AFTER, MAX_CONTACTS, MAX_SNAPSHOT_SCAN, PHONE_IN_TEXT, EMAIL_IN_TEXT, TEL_HREF, MAILTO_HREF, FIELD_LINE, INBOX_EMAIL_READ, MAIL_READ_CALL, RESERVED_EMAIL_TLD, PHONE_LABEL, NON_PHONE_NUMBER_LABEL, IDENTITY_LABEL, LOOKUP_LABEL, NON_NAME_SUFFIX_LABEL, DATE_LIKE, SIGNUP_ACTION_NAME, SIGNUP_SUBMIT_NAME, LOGIN_ACTION_NAME, SIGNUP_GOTO, LOGIN_GOTO, MINTED_INBOX_ALIAS, STAMP_EVAL, RECORDING_VALUE_PLACEHOLDERS, NEGATIVE_TEST_TITLE, NEGATIVE_SIGNUP_ASSERT, STORED_LOGIN_RE, CONFIRM_CLICK_RE, SAVE_UPDATE_RE, NOT_A_CONFIRM_RE, ENTITY_WORDS, CREATE_ENTITY_CLICK_RE, RESTORE_CLICK_RE, MAX_ACCOUNT_ACTIONS, MAX_TRY_BLOCKS, MAX_STATEMENT_LOOKBACK, MAX_FINDINGS5, MAX_TRY_BODY, AUTH_REGION_START, AUTH_REGION_END, INBOX_REGION_START, INBOX_REGION_END, ACCOUNT_ROUTE_RE, ENTITY_WORD_RES, PERSIST_CALL_RE, FILL_IDENT_ARG_RE, PERSIST_ARGS_RE, HOST_IN_URL_ARG, URL_WAIT_TOKEN, GLOB_ESCAPED, URL_WAIT_MAX_PATH, URL_WAIT_MAX_GLOB_STARS, UNREACHED_MAX_PATHS, UNREACHED_MATCH_BUDGET, STALE_TOKEN, RESULT_WAIT, WRITE_CALL_PRESENT, SUBMIT_CALL_PRESENT, TEST_CALL, VALUE_WRITE_CALLS, SUBMIT_CALLS, DISTINCT_PAGE_CALLS, OWN_CONTROL_MATCHERS, OUTCOME_CONTENT_MATCHERS, OUTCOME_STATE_MATCHERS, ANY_CONTENT_ARG, ROOT_SUBJECT, LITERAL_SUBJECT, MATCHER_AFTER_EXPECT, STALE_MAX_EVENTS, CHECKED, UNCHECKED, squash, NOT_JUDGED, STALE_REASON_TEXT, LIVE_WRITE_NAME, BUNDLED_CHECKS_BEFORE_WRITE, STOP, SYNONYMS, AREA_TOKENS;
|
|
15922
|
+
var READ_ONLY_TAGS, PRODUCTION_READ_ONLY_GUARD_TEMPLATE, HOST_AGNOSTIC_URL_TEMPLATE, TEST_QUALITY_CONVENTIONS, NON_PROD_HOST_LABEL, NON_PROD_HOST_TOKEN, TRANSIENT_TEXT, STRING_LITERAL, FIRST_LITERAL, GET_BY_TEXT_LITERAL2, ROLE_NAME_LITERAL2, MAX_LINT_INPUT, MAX_BALANCED_SCAN, MAX_CALLS, MAX_SEEN_SCAN, ACTION_CALL, BY_NAME_LOCATOR, ACTION_AFTER, MAX_CONTACTS, MAX_SNAPSHOT_SCAN, PHONE_IN_TEXT, EMAIL_IN_TEXT, TEL_HREF, MAILTO_HREF, FIELD_LINE, INBOX_EMAIL_READ, MAIL_READ_CALL, RESERVED_EMAIL_TLD, PHONE_LABEL, NON_PHONE_NUMBER_LABEL, IDENTITY_LABEL, LOOKUP_LABEL, NON_NAME_SUFFIX_LABEL, DATE_LIKE, SIGNUP_ACTION_NAME, SIGNUP_SUBMIT_NAME, LOGIN_ACTION_NAME, SIGNUP_GOTO, LOGIN_GOTO, MINTED_INBOX_ALIAS, STAMP_EVAL, RECORDING_VALUE_PLACEHOLDERS, NEGATIVE_TEST_TITLE, NEGATIVE_SIGNUP_ASSERT, STORED_LOGIN_RE, CONFIRM_CLICK_RE, SAVE_UPDATE_RE, NOT_A_CONFIRM_RE, ENTITY_WORDS, CREATE_ENTITY_CLICK_RE, RESTORE_CLICK_RE, MAX_ACCOUNT_ACTIONS, MAX_TRY_BLOCKS, MAX_STATEMENT_LOOKBACK, MAX_FINDINGS5, MAX_TRY_BODY, AUTH_REGION_START, AUTH_REGION_END, INBOX_REGION_START, INBOX_REGION_END, ACCOUNT_ROUTE_RE, ENTITY_WORD_RES, PERSIST_CALL_RE, FILL_IDENT_ARG_RE, PERSIST_ARGS_RE, HOST_IN_URL_ARG, URL_WAIT_TOKEN, GLOB_ESCAPED, URL_WAIT_MAX_PATH, URL_WAIT_MAX_GLOB_STARS, UNREACHED_MAX_PATHS, UNREACHED_MATCH_BUDGET, STALE_TOKEN, RESULT_WAIT, WRITE_CALL_PRESENT, SUBMIT_CALL_PRESENT, TEST_CALL, VALUE_WRITE_CALLS, SUBMIT_CALLS, DISTINCT_PAGE_CALLS, OWN_CONTROL_MATCHERS, OUTCOME_CONTENT_MATCHERS, OUTCOME_STATE_MATCHERS, ANY_CONTENT_ARG, ROOT_SUBJECT, LITERAL_SUBJECT, MATCHER_AFTER_EXPECT, STALE_MAX_EVENTS, CHECKED, UNCHECKED, squash, NOT_JUDGED, STALE_REASON_TEXT, LIVE_WRITE_NAME, BUNDLED_CHECKS_BEFORE_WRITE, MAX_REGEX_MATCHER_FINDINGS, REGEX_EQUALITY_MATCHERS, STOP, SYNONYMS, AREA_TOKENS;
|
|
15542
15923
|
var init_test_authoring_quality = __esm({
|
|
15543
15924
|
"../shared/dist/test-authoring-quality.js"() {
|
|
15544
15925
|
"use strict";
|
|
@@ -15770,6 +16151,8 @@ var init_test_authoring_quality = __esm({
|
|
|
15770
16151
|
};
|
|
15771
16152
|
LIVE_WRITE_NAME = /\b(?:submit|place|book|reserve|confirm|pay|send|checkout|order|register|sign\s?up|create account)\b/i;
|
|
15772
16153
|
BUNDLED_CHECKS_BEFORE_WRITE = 5;
|
|
16154
|
+
MAX_REGEX_MATCHER_FINDINGS = 10;
|
|
16155
|
+
REGEX_EQUALITY_MATCHERS = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual", "toContain", "toHaveJSProperty"]);
|
|
15773
16156
|
STOP = /* @__PURE__ */ new Set([
|
|
15774
16157
|
"a",
|
|
15775
16158
|
"an",
|
|
@@ -15998,6 +16381,19 @@ var init_fence_runs = __esm({
|
|
|
15998
16381
|
}
|
|
15999
16382
|
});
|
|
16000
16383
|
|
|
16384
|
+
// ../shared/dist/trailing-slashes.js
|
|
16385
|
+
function stripTrailingSlashes(value) {
|
|
16386
|
+
let end = value.length;
|
|
16387
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47)
|
|
16388
|
+
end--;
|
|
16389
|
+
return end === value.length ? value : value.slice(0, end);
|
|
16390
|
+
}
|
|
16391
|
+
var init_trailing_slashes = __esm({
|
|
16392
|
+
"../shared/dist/trailing-slashes.js"() {
|
|
16393
|
+
"use strict";
|
|
16394
|
+
}
|
|
16395
|
+
});
|
|
16396
|
+
|
|
16001
16397
|
// ../shared/dist/survey-bug-signature.js
|
|
16002
16398
|
function normalizeWords(text) {
|
|
16003
16399
|
return text.toLowerCase().replace(UUID, " id ").replace(PREFIXED_ID, " id ").replace(HEX_RUN, (run) => /\d/.test(run) ? " id " : run).replace(NUMBER_RUN, " # ").replace(/[^\p{L}\p{N}#]+/gu, " ").replace(/\s+/g, " ").trim();
|
|
@@ -16008,7 +16404,7 @@ function normalizeBugPath(route) {
|
|
|
16008
16404
|
if (hash >= 0 && path[hash + 1] !== "/")
|
|
16009
16405
|
path = path.slice(0, hash);
|
|
16010
16406
|
const segments = path.toLowerCase().split("/").map((segment) => /\d/.test(segment) ? ":id" : segment);
|
|
16011
|
-
const joined = segments.join("/")
|
|
16407
|
+
const joined = stripTrailingSlashes(segments.join("/"));
|
|
16012
16408
|
return joined || (path.startsWith("/") ? "/" : "");
|
|
16013
16409
|
}
|
|
16014
16410
|
function isGenericConsoleMessage(message) {
|
|
@@ -16142,6 +16538,7 @@ var SIGNATURE_SOURCE_MAX_CHARS, UUID, PREFIXED_ID, HEX_RUN, NUMBER_RUN, GENERIC_
|
|
|
16142
16538
|
var init_survey_bug_signature = __esm({
|
|
16143
16539
|
"../shared/dist/survey-bug-signature.js"() {
|
|
16144
16540
|
"use strict";
|
|
16541
|
+
init_trailing_slashes();
|
|
16145
16542
|
SIGNATURE_SOURCE_MAX_CHARS = 800;
|
|
16146
16543
|
UUID = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/g;
|
|
16147
16544
|
PREFIXED_ID = /\b[a-z]{1,8}[-_]\d+\b/g;
|
|
@@ -17422,9 +17819,9 @@ function lastLocatorLabels(chain) {
|
|
|
17422
17819
|
if (t)
|
|
17423
17820
|
labels.push(t[1]);
|
|
17424
17821
|
}
|
|
17425
|
-
const
|
|
17426
|
-
if (
|
|
17427
|
-
labels.push(
|
|
17822
|
+
const has2 = optionText(last.args, HAS_TEXT_OPTION_RE);
|
|
17823
|
+
if (has2 !== null)
|
|
17824
|
+
labels.push(has2);
|
|
17428
17825
|
}
|
|
17429
17826
|
return labels.map((l) => l.replace(/^[\s^(?:]+/, "").trim());
|
|
17430
17827
|
}
|
|
@@ -18181,14 +18578,17 @@ var init_promotion_quality_gate = __esm({
|
|
|
18181
18578
|
SOURCE_RANK = { generator: 0, classifier: 0, healer: 1, human: 2 };
|
|
18182
18579
|
strengthCheck = {
|
|
18183
18580
|
id: "assertion-strength",
|
|
18184
|
-
run({ code, strengthGate, flagContradictedAdditions }) {
|
|
18581
|
+
run({ code, tags, strengthGate, flagContradictedAdditions }) {
|
|
18185
18582
|
const verdict = validateAssertionStrength(code, { flagContradictedAdditions: flagContradictedAdditions === true });
|
|
18186
18583
|
if (verdict.severity === "route-bug")
|
|
18187
18584
|
return [{ check: "route-bug" }];
|
|
18188
|
-
|
|
18585
|
+
const authDowngrade = verdict.severity === "downgrade" && tags.some((t) => t.toLowerCase() === "@auth");
|
|
18586
|
+
if (verdict.severity !== "block" && !authDowngrade)
|
|
18189
18587
|
return [];
|
|
18190
18588
|
const revealed = strengthGate?.severity === "none" && strengthGate.revealEvidence === "revealed" && strengthGate.source !== "heal";
|
|
18191
|
-
|
|
18589
|
+
if (revealed)
|
|
18590
|
+
return [];
|
|
18591
|
+
return [{ check: authDowngrade ? "strength-downgrade-auth" : "strength-block" }];
|
|
18192
18592
|
}
|
|
18193
18593
|
};
|
|
18194
18594
|
STRING_CONST = /\b(?:const|let|var)\s{1,20}([A-Za-z_$][\w$]{0,60})\s{0,20}=\s{0,20}(?:'([^'\\\n]{0,300})'|"([^"\\\n]{0,300})"|`([^`\\$\n]{0,300})`)\s{0,20}[;\n]/g;
|
|
@@ -18595,6 +18995,7 @@ __export(dist_exports, {
|
|
|
18595
18995
|
AUDIO_CHUNK_DURATION_MS: () => AUDIO_CHUNK_DURATION_MS,
|
|
18596
18996
|
AUTH_PATH_SIGNATURE: () => AUTH_PATH_SIGNATURE,
|
|
18597
18997
|
AUTH_PROVIDER_HOST_SIGNATURES: () => AUTH_PROVIDER_HOST_SIGNATURES,
|
|
18998
|
+
AUTH_SESSION_GENERATION_RE: () => AUTH_SESSION_GENERATION_RE,
|
|
18598
18999
|
AUTH_STATE_EXPIRY_SKEW_MS: () => AUTH_STATE_EXPIRY_SKEW_MS,
|
|
18599
19000
|
AUTH_TAG_COOKIE_STATE: () => AUTH_TAG_COOKIE_STATE,
|
|
18600
19001
|
AUTH_TAG_LOGIN_BEARER: () => AUTH_TAG_LOGIN_BEARER,
|
|
@@ -18727,6 +19128,8 @@ __export(dist_exports, {
|
|
|
18727
19128
|
LIVE_FRAME_STALE_MS: () => LIVE_FRAME_STALE_MS,
|
|
18728
19129
|
LIVE_FRAME_UNFOCUSED_INTERVAL_MS: () => LIVE_FRAME_UNFOCUSED_INTERVAL_MS,
|
|
18729
19130
|
LIVE_THUMBNAIL_MAX_BASE64_BYTES: () => LIVE_THUMBNAIL_MAX_BASE64_BYTES,
|
|
19131
|
+
MAX_BALANCED_SCAN: () => MAX_BALANCED_SCAN,
|
|
19132
|
+
MAX_CALLS: () => MAX_CALLS,
|
|
18730
19133
|
MAX_CREDIT_TOPUP_CENTS: () => MAX_CREDIT_TOPUP_CENTS,
|
|
18731
19134
|
MAX_DEEPLINK_LENGTH: () => MAX_DEEPLINK_LENGTH,
|
|
18732
19135
|
MAX_EMAIL_ADDRESS_LENGTH: () => MAX_EMAIL_ADDRESS_LENGTH,
|
|
@@ -18771,6 +19174,7 @@ __export(dist_exports, {
|
|
|
18771
19174
|
MUSE_ENGINE_DEFAULT_MODEL: () => MUSE_ENGINE_DEFAULT_MODEL,
|
|
18772
19175
|
MUSE_ENGINE_MODELS: () => MUSE_ENGINE_MODELS,
|
|
18773
19176
|
NOREPLY_EMAIL: () => NOREPLY_EMAIL,
|
|
19177
|
+
OPENCODE_GO_2_PREFIX: () => OPENCODE_GO_2_PREFIX,
|
|
18774
19178
|
OPENCODE_GO_MODEL_PATTERN: () => OPENCODE_GO_MODEL_PATTERN,
|
|
18775
19179
|
OPENCODE_GO_PREFIX: () => OPENCODE_GO_PREFIX,
|
|
18776
19180
|
OPENROUTER_MAX_CALL_COST_USD: () => OPENROUTER_MAX_CALL_COST_USD,
|
|
@@ -18806,6 +19210,8 @@ __export(dist_exports, {
|
|
|
18806
19210
|
PROMOTION_GATE_MAX_CODE_CHARS: () => PROMOTION_GATE_MAX_CODE_CHARS,
|
|
18807
19211
|
PROMOTION_QUALITY_CHECKS: () => PROMOTION_QUALITY_CHECKS,
|
|
18808
19212
|
PROVIDER_CHAINS: () => PROVIDER_CHAINS,
|
|
19213
|
+
PROVIDER_CHAIN_LEADS: () => PROVIDER_CHAIN_LEADS,
|
|
19214
|
+
PROVIDER_HOST_NOT_OFFERED: () => PROVIDER_HOST_NOT_OFFERED,
|
|
18809
19215
|
PUBLIC_PLANS: () => PUBLIC_PLANS,
|
|
18810
19216
|
QUARANTINE_CLOSE_TAGS: () => QUARANTINE_CLOSE_TAGS,
|
|
18811
19217
|
READINESS_POLL_MAX_CODE_CHARS: () => READINESS_POLL_MAX_CODE_CHARS,
|
|
@@ -18844,6 +19250,7 @@ __export(dist_exports, {
|
|
|
18844
19250
|
SURVEY_NOTES_RENDER_MAX_CHARS: () => SURVEY_NOTES_RENDER_MAX_CHARS,
|
|
18845
19251
|
SURVEY_NOTE_KEYS: () => SURVEY_NOTE_KEYS,
|
|
18846
19252
|
SUSPICIOUS_PASS_TAG: () => SUSPICIOUS_PASS_TAG,
|
|
19253
|
+
TEST_DID_NOT_RUN_ERROR: () => TEST_DID_NOT_RUN_ERROR,
|
|
18847
19254
|
TEST_QUALITY_CONVENTIONS: () => TEST_QUALITY_CONVENTIONS,
|
|
18848
19255
|
TEST_USER_ENV_KEYS: () => TEST_USER_ENV_KEYS,
|
|
18849
19256
|
TEST_VARIABLE_SECRET_KEY_PATTERN: () => TEST_VARIABLE_SECRET_KEY_PATTERN,
|
|
@@ -18859,6 +19266,8 @@ __export(dist_exports, {
|
|
|
18859
19266
|
VOICE_PREVIEW_SAMPLE: () => VOICE_PREVIEW_SAMPLE,
|
|
18860
19267
|
WEB_PROXY_CHAIN_HEADER: () => WEB_PROXY_CHAIN_HEADER,
|
|
18861
19268
|
WITHHELD_CREDENTIAL_TEXT: () => WITHHELD_CREDENTIAL_TEXT,
|
|
19269
|
+
ZAI_MODEL_PATTERN: () => ZAI_MODEL_PATTERN,
|
|
19270
|
+
ZAI_PREFIX: () => ZAI_PREFIX,
|
|
18862
19271
|
accumulateStatements: () => accumulateStatements,
|
|
18863
19272
|
adoptBrokerFailover: () => adoptBrokerFailover,
|
|
18864
19273
|
aiCostToCreditCents: () => aiCostToCreditCents,
|
|
@@ -18867,6 +19276,7 @@ __export(dist_exports, {
|
|
|
18867
19276
|
analyzeMobileSelectorQuality: () => analyzeMobileSelectorQuality,
|
|
18868
19277
|
analyzePersistenceReread: () => analyzePersistenceReread,
|
|
18869
19278
|
analyzeReadinessPolling: () => analyzeReadinessPolling,
|
|
19279
|
+
analyzeRegexMatchers: () => analyzeRegexMatchers,
|
|
18870
19280
|
analyzeSharedAccountUse: () => analyzeSharedAccountUse,
|
|
18871
19281
|
analyzeStaleAfterSubmit: () => analyzeStaleAfterSubmit,
|
|
18872
19282
|
analyzeTestData: () => analyzeTestData,
|
|
@@ -19030,6 +19440,7 @@ __export(dist_exports, {
|
|
|
19030
19440
|
formatPersistenceRereadAdvisory: () => formatPersistenceRereadAdvisory,
|
|
19031
19441
|
formatPoolBudgetLine: () => formatPoolBudgetLine,
|
|
19032
19442
|
formatReadinessPollAdvisory: () => formatReadinessPollAdvisory,
|
|
19443
|
+
formatRegexMatcherNote: () => formatRegexMatcherNote,
|
|
19033
19444
|
formatStaleAfterSubmitAdvisory: () => formatStaleAfterSubmitAdvisory,
|
|
19034
19445
|
formatSuspiciousExpectation: () => formatSuspiciousExpectation,
|
|
19035
19446
|
formatUrlWaitAdvisory: () => formatUrlWaitAdvisory,
|
|
@@ -19068,12 +19479,14 @@ __export(dist_exports, {
|
|
|
19068
19479
|
hasEncodedRequestBody: () => hasEncodedRequestBody,
|
|
19069
19480
|
hasEngineSpecParts: () => hasEngineSpecParts,
|
|
19070
19481
|
hasOnlyConstantAssertions: () => hasOnlyConstantAssertions,
|
|
19482
|
+
hasOpenCodeGo2Prefix: () => hasOpenCodeGo2Prefix,
|
|
19071
19483
|
hasOpenCodeGoPrefix: () => hasOpenCodeGoPrefix,
|
|
19072
19484
|
hasOpenRouterPrefix: () => hasOpenRouterPrefix,
|
|
19073
19485
|
hasPlatformLocator: () => hasPlatformLocator,
|
|
19074
19486
|
hasReadOnlyMarker: () => hasReadOnlyMarker,
|
|
19075
19487
|
hasSurveyNotes: () => hasSurveyNotes,
|
|
19076
19488
|
hasTestFailContract: () => hasTestFailContract,
|
|
19489
|
+
hasZaiPrefix: () => hasZaiPrefix,
|
|
19077
19490
|
healMobileTestSteps: () => healMobileTestSteps,
|
|
19078
19491
|
healthRunWhere: () => healthRunWhere,
|
|
19079
19492
|
heldCodeRecord: () => heldCodeRecord,
|
|
@@ -19177,6 +19590,7 @@ __export(dist_exports, {
|
|
|
19177
19590
|
isValidSttProvider: () => isValidSttProvider,
|
|
19178
19591
|
isValidTtsModel: () => isValidTtsModel,
|
|
19179
19592
|
isValidTtsVoiceId: () => isValidTtsVoiceId,
|
|
19593
|
+
isZaiModelId: () => isZaiModelId,
|
|
19180
19594
|
jsEscapedForms: () => jsEscapedForms,
|
|
19181
19595
|
jsonStructureRefusalOf: () => jsonStructureRefusalOf,
|
|
19182
19596
|
jsonStructureVerify: () => jsonStructureVerify,
|
|
@@ -19225,6 +19639,7 @@ __export(dist_exports, {
|
|
|
19225
19639
|
parseApiCardModelUpdate: () => parseApiCardModelUpdate,
|
|
19226
19640
|
parseAppTimezone: () => parseAppTimezone,
|
|
19227
19641
|
parseAuditGroupScopeId: () => parseAuditGroupScopeId,
|
|
19642
|
+
parseAuthSessionStamp: () => parseAuthSessionStamp,
|
|
19228
19643
|
parseBoolFlag: () => parseBoolFlag,
|
|
19229
19644
|
parseDbPoolSize: () => parseDbPoolSize,
|
|
19230
19645
|
parseDeepgramListenResponse: () => parseDeepgramListenResponse,
|
|
@@ -19268,7 +19683,11 @@ __export(dist_exports, {
|
|
|
19268
19683
|
profileKeyRoute: () => profileKeyRoute,
|
|
19269
19684
|
projectAppHosts: () => projectAppHosts,
|
|
19270
19685
|
providerChainFor: () => providerChainFor,
|
|
19686
|
+
providerChainHostOf: () => providerChainHostOf,
|
|
19687
|
+
providerChainLeadsFor: () => providerChainLeadsFor,
|
|
19271
19688
|
providerChainPrimariesForHop: () => providerChainPrimariesForHop,
|
|
19689
|
+
providerHostPlan: () => providerHostPlan,
|
|
19690
|
+
readBalanced: () => readBalanced,
|
|
19272
19691
|
readChatFailover: () => readChatFailover,
|
|
19273
19692
|
readChatReasoningTokens: () => readChatReasoningTokens,
|
|
19274
19693
|
readChatUsage: () => readChatUsage,
|
|
@@ -19351,6 +19770,7 @@ __export(dist_exports, {
|
|
|
19351
19770
|
stripCoverageComments: () => stripCoverageComments,
|
|
19352
19771
|
stripEnvironmentalPrefix: () => stripEnvironmentalPrefix,
|
|
19353
19772
|
stripLoginScaffolding: () => stripLoginScaffolding,
|
|
19773
|
+
stripTrailingSlashes: () => stripTrailingSlashes,
|
|
19354
19774
|
strongerNeedsReviewSource: () => strongerNeedsReviewSource,
|
|
19355
19775
|
sttModelsForProvider: () => sttModelsForProvider,
|
|
19356
19776
|
summarizeHealth: () => summarizeHealth,
|
|
@@ -19378,7 +19798,8 @@ __export(dist_exports, {
|
|
|
19378
19798
|
withReviewSource: () => withReviewSource,
|
|
19379
19799
|
withoutNotChargedClaim: () => withoutNotChargedClaim,
|
|
19380
19800
|
withoutServerOnlySpendFields: () => withoutServerOnlySpendFields,
|
|
19381
|
-
withoutSurveyNotes: () => withoutSurveyNotes
|
|
19801
|
+
withoutSurveyNotes: () => withoutSurveyNotes,
|
|
19802
|
+
zaiWireModel: () => zaiWireModel
|
|
19382
19803
|
});
|
|
19383
19804
|
var init_dist = __esm({
|
|
19384
19805
|
"../shared/dist/index.js"() {
|
|
@@ -19420,6 +19841,7 @@ var init_dist = __esm({
|
|
|
19420
19841
|
init_admin_view_return_to();
|
|
19421
19842
|
init_auth_plan();
|
|
19422
19843
|
init_auth_block_detector();
|
|
19844
|
+
init_auth_session_stamp();
|
|
19423
19845
|
init_auth_state_freshness();
|
|
19424
19846
|
init_skip_coverage_detector();
|
|
19425
19847
|
init_feature_coverage();
|
|
@@ -19476,6 +19898,7 @@ var init_dist = __esm({
|
|
|
19476
19898
|
init_promotion_quality_gate();
|
|
19477
19899
|
init_regex_alternation();
|
|
19478
19900
|
init_page_evidence();
|
|
19901
|
+
init_trailing_slashes();
|
|
19479
19902
|
}
|
|
19480
19903
|
});
|
|
19481
19904
|
|
|
@@ -19911,6 +20334,7 @@ var require_route_manifest = __commonJS({
|
|
|
19911
20334
|
exports2.renderRouteManifestChecklist = renderRouteManifestChecklist;
|
|
19912
20335
|
exports2.renderRepoReadsChecklist = renderRepoReadsChecklist;
|
|
19913
20336
|
exports2.buildRepoReadsRouteAllowlist = buildRepoReadsRouteAllowlist;
|
|
20337
|
+
var shared_1 = (init_dist(), __toCommonJS(dist_exports));
|
|
19914
20338
|
var promises_1 = require("fs/promises");
|
|
19915
20339
|
var node_path_1 = require("path");
|
|
19916
20340
|
var untrusted_knowledge_js_1 = require_untrusted_knowledge();
|
|
@@ -19965,7 +20389,7 @@ var require_route_manifest = __commonJS({
|
|
|
19965
20389
|
}
|
|
19966
20390
|
function collapsePath(path) {
|
|
19967
20391
|
const collapsed = path.replace(/\/{2,}/g, "/");
|
|
19968
|
-
return collapsed.length > 1 ?
|
|
20392
|
+
return collapsed.length > 1 ? (0, shared_1.stripTrailingSlashes)(collapsed) : collapsed;
|
|
19969
20393
|
}
|
|
19970
20394
|
function normalizeManifestPath(raw) {
|
|
19971
20395
|
const text = str(raw, 500);
|
|
@@ -20314,6 +20738,7 @@ var require_exploration_parser = __commonJS({
|
|
|
20314
20738
|
exports2.normalizeRouteForFocusScope = normalizeRouteForFocusScope;
|
|
20315
20739
|
exports2.routeIsWithinFocusAreas = routeIsWithinFocusAreas;
|
|
20316
20740
|
exports2.detectRouteFromResponse = detectRouteFromResponse;
|
|
20741
|
+
var shared_1 = (init_dist(), __toCommonJS(dist_exports));
|
|
20317
20742
|
function extractCodeLines(toolResultContent) {
|
|
20318
20743
|
const isPlaywrightLine = (l) => l.startsWith("await page.") || l.startsWith("await expect(");
|
|
20319
20744
|
const codeSection = toolResultContent.match(/###\s*Ran Playwright code\s*\n([\s\S]*?)(?=\n###|\n$|$)/);
|
|
@@ -20607,7 +21032,7 @@ var require_exploration_parser = __commonJS({
|
|
|
20607
21032
|
const decoded = raw.replace(/&/g, "&").replace(/&#(\d+);/g, (_m, code) => String.fromCharCode(Number(code)));
|
|
20608
21033
|
try {
|
|
20609
21034
|
const url = new URL(decoded, "http://canonical.invalid");
|
|
20610
|
-
url.pathname = url.pathname
|
|
21035
|
+
url.pathname = (0, shared_1.stripTrailingSlashes)(url.pathname) || "/";
|
|
20611
21036
|
return normalizeRoute(url);
|
|
20612
21037
|
} catch {
|
|
20613
21038
|
return decoded;
|
|
@@ -20639,9 +21064,9 @@ var require_exploration_parser = __commonJS({
|
|
|
20639
21064
|
const text = String(route ?? "").trim();
|
|
20640
21065
|
const at = text.indexOf("#/");
|
|
20641
21066
|
if (at < 0)
|
|
20642
|
-
return text.startsWith("#") ? text.split("?")[0].toLowerCase()
|
|
21067
|
+
return text.startsWith("#") ? (0, shared_1.stripTrailingSlashes)(text.split("?")[0].toLowerCase()) : null;
|
|
20643
21068
|
const hashPath = text.slice(at).split("?")[0].toLowerCase();
|
|
20644
|
-
return hashPath.length > 2 ?
|
|
21069
|
+
return hashPath.length > 2 ? (0, shared_1.stripTrailingSlashes)(hashPath) || "#/" : hashPath;
|
|
20645
21070
|
}
|
|
20646
21071
|
function routeIsWithinFocusAreas(route, focusAreas, otherRoutes) {
|
|
20647
21072
|
if (!focusAreas || focusAreas.length === 0)
|
|
@@ -20898,8 +21323,11 @@ var require_survey_network_guard = __commonJS({
|
|
|
20898
21323
|
exports2.maskedPath = maskedPath;
|
|
20899
21324
|
exports2.bodyKeysOf = bodyKeysOf;
|
|
20900
21325
|
exports2.graphqlDocumentIsQueryOnly = graphqlDocumentIsQueryOnly;
|
|
21326
|
+
exports2.graphqlOperationRoots = graphqlOperationRoots;
|
|
20901
21327
|
exports2.isTokenRefreshWrite = isTokenRefreshWrite;
|
|
20902
21328
|
exports2.isSignInPathWrite = isSignInPathWrite;
|
|
21329
|
+
exports2.graphqlUrlCarriesQuery = graphqlUrlCarriesQuery;
|
|
21330
|
+
exports2.graphqlGetMayWrite = graphqlGetMayWrite;
|
|
20903
21331
|
exports2.writeShapedGet = writeShapedGet;
|
|
20904
21332
|
exports2.decideSurveyRequest = decideSurveyRequest;
|
|
20905
21333
|
exports2.installSurveyNetworkGuard = installSurveyNetworkGuard;
|
|
@@ -21176,6 +21604,100 @@ var require_survey_network_guard = __commonJS({
|
|
|
21176
21604
|
}
|
|
21177
21605
|
return depth === 0 && parens === 0 && objects === 0 && sawOperation;
|
|
21178
21606
|
}
|
|
21607
|
+
function graphqlOperationRoots(text) {
|
|
21608
|
+
const tokens2 = lexGraphql(text);
|
|
21609
|
+
if (!tokens2)
|
|
21610
|
+
return null;
|
|
21611
|
+
const ops = [];
|
|
21612
|
+
let depth = 0;
|
|
21613
|
+
let parens = 0;
|
|
21614
|
+
let objects = 0;
|
|
21615
|
+
let defStart = true;
|
|
21616
|
+
let kind = null;
|
|
21617
|
+
let current = null;
|
|
21618
|
+
let prev = "";
|
|
21619
|
+
for (const tok of tokens2) {
|
|
21620
|
+
const last = prev;
|
|
21621
|
+
prev = tok;
|
|
21622
|
+
if (tok === "(") {
|
|
21623
|
+
parens++;
|
|
21624
|
+
continue;
|
|
21625
|
+
}
|
|
21626
|
+
if (tok === ")") {
|
|
21627
|
+
parens--;
|
|
21628
|
+
if (parens < 0 || objects !== 0)
|
|
21629
|
+
return null;
|
|
21630
|
+
continue;
|
|
21631
|
+
}
|
|
21632
|
+
if (parens > 0) {
|
|
21633
|
+
if (tok === "{")
|
|
21634
|
+
objects++;
|
|
21635
|
+
else if (tok === "}") {
|
|
21636
|
+
objects--;
|
|
21637
|
+
if (objects < 0)
|
|
21638
|
+
return null;
|
|
21639
|
+
}
|
|
21640
|
+
continue;
|
|
21641
|
+
}
|
|
21642
|
+
if (tok === "{") {
|
|
21643
|
+
if (depth === 0) {
|
|
21644
|
+
if (defStart)
|
|
21645
|
+
kind = "query";
|
|
21646
|
+
else if (kind === null)
|
|
21647
|
+
return null;
|
|
21648
|
+
current = kind === "fragment" ? null : { kind, roots: [] };
|
|
21649
|
+
if (current)
|
|
21650
|
+
ops.push(current);
|
|
21651
|
+
defStart = false;
|
|
21652
|
+
}
|
|
21653
|
+
depth++;
|
|
21654
|
+
continue;
|
|
21655
|
+
}
|
|
21656
|
+
if (tok === "}") {
|
|
21657
|
+
depth--;
|
|
21658
|
+
if (depth < 0)
|
|
21659
|
+
return null;
|
|
21660
|
+
if (depth === 0) {
|
|
21661
|
+
defStart = true;
|
|
21662
|
+
kind = null;
|
|
21663
|
+
current = null;
|
|
21664
|
+
}
|
|
21665
|
+
continue;
|
|
21666
|
+
}
|
|
21667
|
+
if (depth === 0) {
|
|
21668
|
+
if (!/^[_A-Za-z]/.test(tok)) {
|
|
21669
|
+
if (tok === "@")
|
|
21670
|
+
continue;
|
|
21671
|
+
return null;
|
|
21672
|
+
}
|
|
21673
|
+
if (defStart) {
|
|
21674
|
+
if (tok !== "query" && tok !== "mutation" && tok !== "fragment")
|
|
21675
|
+
return null;
|
|
21676
|
+
kind = tok;
|
|
21677
|
+
defStart = false;
|
|
21678
|
+
}
|
|
21679
|
+
continue;
|
|
21680
|
+
}
|
|
21681
|
+
if (depth > 1 || !current)
|
|
21682
|
+
continue;
|
|
21683
|
+
if (/^[_A-Za-z]/.test(tok)) {
|
|
21684
|
+
if (last === "@")
|
|
21685
|
+
continue;
|
|
21686
|
+
if (last === ":") {
|
|
21687
|
+
if (current.roots.length === 0)
|
|
21688
|
+
return null;
|
|
21689
|
+
current.roots[current.roots.length - 1] = tok;
|
|
21690
|
+
continue;
|
|
21691
|
+
}
|
|
21692
|
+
current.roots.push(tok);
|
|
21693
|
+
continue;
|
|
21694
|
+
}
|
|
21695
|
+
if (tok === ":" || tok === "@")
|
|
21696
|
+
continue;
|
|
21697
|
+
return null;
|
|
21698
|
+
}
|
|
21699
|
+
return depth === 0 && parens === 0 && objects === 0 ? ops : null;
|
|
21700
|
+
}
|
|
21179
21701
|
function isGraphqlQueryOnly(postData) {
|
|
21180
21702
|
if (!postData || postData.length > MAX_BODY_CHARS)
|
|
21181
21703
|
return false;
|
|
@@ -21213,6 +21735,34 @@ var require_survey_network_guard = __commonJS({
|
|
|
21213
21735
|
"remove"
|
|
21214
21736
|
]);
|
|
21215
21737
|
var NOT_A_SESSION_END = (segs) => !segs.some((seg) => SESSION_END_SEGMENTS.has(seg));
|
|
21738
|
+
var DESTRUCTIVE_WORDS = /* @__PURE__ */ new Set([
|
|
21739
|
+
"delete",
|
|
21740
|
+
"remove",
|
|
21741
|
+
"destroy",
|
|
21742
|
+
"deactivate",
|
|
21743
|
+
"terminate",
|
|
21744
|
+
"purge",
|
|
21745
|
+
"wipe",
|
|
21746
|
+
"erase",
|
|
21747
|
+
"cancel",
|
|
21748
|
+
"close",
|
|
21749
|
+
"revoke",
|
|
21750
|
+
"unlink",
|
|
21751
|
+
"disconnect"
|
|
21752
|
+
]);
|
|
21753
|
+
var DESTRUCTIVE_PREFIXES = ["delete", "remove", "destroy", "deactivate", "terminate", "purge"];
|
|
21754
|
+
function hasDestructiveSegment(rawSegs) {
|
|
21755
|
+
return rawSegs.some((raw) => {
|
|
21756
|
+
let seg = raw;
|
|
21757
|
+
try {
|
|
21758
|
+
seg = decodeURIComponent(raw);
|
|
21759
|
+
} catch {
|
|
21760
|
+
}
|
|
21761
|
+
const words = seg.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/);
|
|
21762
|
+
return words.some((word) => DESTRUCTIVE_WORDS.has(word) || DESTRUCTIVE_PREFIXES.some((p) => word.startsWith(p)));
|
|
21763
|
+
});
|
|
21764
|
+
}
|
|
21765
|
+
var rawPathSegments = (pathname) => pathname.split("/").map((seg) => seg.split(";")[0]).filter(Boolean);
|
|
21216
21766
|
var pathSegments = (pathname) => pathname.toLowerCase().split("/").map((seg) => seg.split(";")[0]).filter(Boolean);
|
|
21217
21767
|
function isTokenRefreshWrite(method, pathname, recentAction = false) {
|
|
21218
21768
|
if (method.toUpperCase() !== "POST")
|
|
@@ -21229,6 +21779,8 @@ var require_survey_network_guard = __commonJS({
|
|
|
21229
21779
|
var AUTH_PAGE_PATH_RE = /(?:^|\/)(?:log-?in|sign-?in|sign_in|auth|oauth2?|sso|callback)(?:\/|$)/;
|
|
21230
21780
|
var SIGN_IN_SEGMENT_RE = /^(?:log-?in|sign-?in|sign_in)$/;
|
|
21231
21781
|
var CALLBACK_ROOT_SEGMENTS = /* @__PURE__ */ new Set(["auth", "oauth", "oauth2", "sso"]);
|
|
21782
|
+
var CALLBACK_PROTOCOL_SEGMENTS = /* @__PURE__ */ new Set(["oidc", "openid", "openid-connect", "openidconnect", "saml", "saml2", "oauth", "oauth2"]);
|
|
21783
|
+
var API_VERSION_SEGMENT_RE = /^v[0-9]{1,3}$/;
|
|
21232
21784
|
var PROVIDER_SEGMENT_RE = /^[a-z][a-z0-9_-]{0,39}$/;
|
|
21233
21785
|
var NOT_A_PROVIDER_SEGMENTS = /* @__PURE__ */ new Set([
|
|
21234
21786
|
"link",
|
|
@@ -21267,14 +21819,21 @@ var require_survey_network_guard = __commonJS({
|
|
|
21267
21819
|
if (!NOT_A_SESSION_END(segs))
|
|
21268
21820
|
return false;
|
|
21269
21821
|
if (AUTH_END_PATH_RE.test(`/${segs.join("/")}`))
|
|
21270
|
-
return
|
|
21822
|
+
return !hasDestructiveSegment(rawPathSegments(pathname).slice(0, -1));
|
|
21823
|
+
let top = segs[0] === "api" ? 1 : 0;
|
|
21824
|
+
if (top < segs.length && API_VERSION_SEGMENT_RE.test(segs[top]))
|
|
21825
|
+
top += 1;
|
|
21826
|
+
const below = segs.length - top;
|
|
21827
|
+
const isProvider = (seg) => PROVIDER_SEGMENT_RE.test(seg) && !NOT_A_PROVIDER_SEGMENTS.has(seg);
|
|
21271
21828
|
const provider = segs[segs.length - 1];
|
|
21272
|
-
const authRootAtTop =
|
|
21273
|
-
if (segs.length >= 3 && authRootAtTop && CALLBACK_ROOT_SEGMENTS.has(segs[segs.length - 3]) &&
|
|
21829
|
+
const authRootAtTop = below === 3 || segs[segs.length - 4] === "api" && segs[segs.length - 3] === "auth";
|
|
21830
|
+
if (segs.length >= 3 && authRootAtTop && CALLBACK_ROOT_SEGMENTS.has(segs[segs.length - 3]) && isProvider(provider)) {
|
|
21274
21831
|
const step = segs[segs.length - 2];
|
|
21275
21832
|
if (step === "callback" || step === "signin")
|
|
21276
21833
|
return true;
|
|
21277
21834
|
}
|
|
21835
|
+
if (below === 4 && CALLBACK_ROOT_SEGMENTS.has(segs[top]) && CALLBACK_PROTOCOL_SEGMENTS.has(segs[top + 1]) && isProvider(segs[top + 2]) && segs[top + 3] === "callback")
|
|
21836
|
+
return true;
|
|
21278
21837
|
if (segs.length < 2 || segs[segs.length - 1] !== "callback")
|
|
21279
21838
|
return false;
|
|
21280
21839
|
const parent = segs[segs.length - 2];
|
|
@@ -21301,7 +21860,7 @@ var require_survey_network_guard = __commonJS({
|
|
|
21301
21860
|
try {
|
|
21302
21861
|
const a = new URL(url);
|
|
21303
21862
|
const b = new URL(appBaseUrl);
|
|
21304
|
-
const path = (u) => u.pathname
|
|
21863
|
+
const path = (u) => (0, shared_1.stripTrailingSlashes)(u.pathname);
|
|
21305
21864
|
return a.origin === b.origin && path(a) === path(b);
|
|
21306
21865
|
} catch {
|
|
21307
21866
|
return false;
|
|
@@ -21323,9 +21882,15 @@ var require_survey_network_guard = __commonJS({
|
|
|
21323
21882
|
const bodyHash = (0, node_crypto_1.createHash)("sha256").update(req.postData ?? "").digest("hex").slice(0, 16);
|
|
21324
21883
|
return `${record.method} ${url.hostname.toLowerCase()}${url.pathname} ${bodyHash}`;
|
|
21325
21884
|
}
|
|
21326
|
-
function
|
|
21327
|
-
|
|
21328
|
-
|
|
21885
|
+
function graphqlUrlCarriesQuery(params) {
|
|
21886
|
+
return params.has("query");
|
|
21887
|
+
}
|
|
21888
|
+
function graphqlGetMayWrite(url, body) {
|
|
21889
|
+
const docs = url.searchParams.getAll("query");
|
|
21890
|
+
return docs.length > 0 && (typeof body === "string" && body.length > 0 || docs.some((doc) => !graphqlDocumentIsQueryOnly(doc)));
|
|
21891
|
+
}
|
|
21892
|
+
function isGraphqlGetWrite(url, s, body) {
|
|
21893
|
+
return isGraphqlEndpoint(url, s) && graphqlGetMayWrite(url, body);
|
|
21329
21894
|
}
|
|
21330
21895
|
exports2.WRITE_VERBS = /* @__PURE__ */ new Set([
|
|
21331
21896
|
"cancel",
|
|
@@ -21529,7 +22094,7 @@ var require_survey_network_guard = __commonJS({
|
|
|
21529
22094
|
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
21530
22095
|
return { action: "allow", rule: readMethod ? "read" : "non-http" };
|
|
21531
22096
|
const appSite = isAppSite(url.hostname, s.appBaseUrl);
|
|
21532
|
-
const graphqlGet = method === "GET" && isGraphqlGetWrite(url, s);
|
|
22097
|
+
const graphqlGet = method === "GET" && isGraphqlGetWrite(url, s, req.postData);
|
|
21533
22098
|
const inActionWindow = s.lastActionAt > 0 && s.now - s.lastActionAt <= ACTION_NAVIGATION_WINDOW_MS;
|
|
21534
22099
|
const typedLoad = s.agentLoad === true && req.resourceType === "document";
|
|
21535
22100
|
const writeShaped = method === "GET" && appSite && !graphqlGet && (inActionWindow || typedLoad) ? writeShapedGet(req.url, {
|
|
@@ -21563,7 +22128,7 @@ var require_survey_network_guard = __commonJS({
|
|
|
21563
22128
|
return { action: "allow", rule: "extra-pattern", reason: extra.reason, record };
|
|
21564
22129
|
if (graphqlGet)
|
|
21565
22130
|
return { action: "block", rule: "app-write", record };
|
|
21566
|
-
if (method === "POST" && isGraphqlEndpoint(url, s) && isGraphqlQueryOnly(req.postData))
|
|
22131
|
+
if (method === "POST" && isGraphqlEndpoint(url, s) && !graphqlUrlCarriesQuery(url.searchParams) && isGraphqlQueryOnly(req.postData))
|
|
21567
22132
|
return { action: "allow", rule: "graphql-query", record };
|
|
21568
22133
|
const passive = s.lastNavigationAt > 0 && (s.lastActionAt === 0 || s.lastNavigationAt >= s.lastActionAt) && s.now - s.lastNavigationAt <= s.passiveWindowMs;
|
|
21569
22134
|
const signature = learnableSignature(req, url, record, s);
|
|
@@ -21595,7 +22160,7 @@ var require_survey_network_guard = __commonJS({
|
|
|
21595
22160
|
const re = new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, ""));
|
|
21596
22161
|
return { reason, test: (hostPath) => re.test(hostPath) };
|
|
21597
22162
|
}
|
|
21598
|
-
const p = pattern.trim().toLowerCase()
|
|
22163
|
+
const p = (0, shared_1.stripTrailingSlashes)(pattern.trim().toLowerCase());
|
|
21599
22164
|
if (p.includes("/"))
|
|
21600
22165
|
return { reason, test: (hostPath) => hostPath.toLowerCase() === p || hostPath.toLowerCase().startsWith(`${p}/`) };
|
|
21601
22166
|
return { reason, test: (hostPath) => {
|
|
@@ -22143,6 +22708,7 @@ var require_survey_repo_route_frontier = __commonJS({
|
|
|
22143
22708
|
exports2.buildRepoRouteCandidates = buildRepoRouteCandidates;
|
|
22144
22709
|
exports2.routeIdsHiddenFromChecklist = routeIdsHiddenFromChecklist;
|
|
22145
22710
|
exports2.parseRepoRouteCoverage = parseRepoRouteCoverage;
|
|
22711
|
+
var shared_1 = (init_dist(), __toCommonJS(dist_exports));
|
|
22146
22712
|
var route_manifest_js_1 = require_route_manifest();
|
|
22147
22713
|
var survey_scope_js_1 = require_survey_scope();
|
|
22148
22714
|
var repo_route_safety_js_1 = require_repo_route_safety();
|
|
@@ -22204,7 +22770,7 @@ var require_survey_repo_route_frontier = __commonJS({
|
|
|
22204
22770
|
return route;
|
|
22205
22771
|
try {
|
|
22206
22772
|
const base = new URL(baseUrl);
|
|
22207
|
-
return `${base.pathname
|
|
22773
|
+
return `${(0, shared_1.stripTrailingSlashes)(base.pathname) || "/"}${route}`;
|
|
22208
22774
|
} catch {
|
|
22209
22775
|
return `/${route}`;
|
|
22210
22776
|
}
|
|
@@ -22435,7 +23001,7 @@ var require_survey_repo_route_frontier = __commonJS({
|
|
|
22435
23001
|
try {
|
|
22436
23002
|
const base = new URL(input.baseUrl);
|
|
22437
23003
|
origin = base.origin;
|
|
22438
|
-
basePathname = base.pathname
|
|
23004
|
+
basePathname = (0, shared_1.stripTrailingSlashes)(base.pathname) || "/";
|
|
22439
23005
|
} catch {
|
|
22440
23006
|
origin = null;
|
|
22441
23007
|
}
|
|
@@ -22468,7 +23034,7 @@ var require_survey_repo_route_frontier = __commonJS({
|
|
|
22468
23034
|
return null;
|
|
22469
23035
|
if (parsed.origin !== this.appOrigin)
|
|
22470
23036
|
return null;
|
|
22471
|
-
const pathname = parsed.pathname
|
|
23037
|
+
const pathname = (0, shared_1.stripTrailingSlashes)(parsed.pathname) || "/";
|
|
22472
23038
|
const key = manifestRouteKey(`${parsed.pathname}${parsed.search}${parsed.hash}`);
|
|
22473
23039
|
if (!key)
|
|
22474
23040
|
return null;
|
|
@@ -22933,6 +23499,7 @@ var require_repo_feature_verification = __commonJS({
|
|
|
22933
23499
|
exports2.parseBrowserVerificationRequirement = parseBrowserVerificationRequirement;
|
|
22934
23500
|
exports2.parseBrowserVerificationResult = parseBrowserVerificationResult;
|
|
22935
23501
|
exports2.renderBrowserVerificationForPrompt = renderBrowserVerificationForPrompt;
|
|
23502
|
+
var shared_1 = (init_dist(), __toCommonJS(dist_exports));
|
|
22936
23503
|
var survey_repo_route_frontier_js_1 = require_survey_repo_route_frontier();
|
|
22937
23504
|
var ABSOLUTE_URL = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
22938
23505
|
function originOf(url) {
|
|
@@ -22989,7 +23556,7 @@ var require_repo_feature_verification = __commonJS({
|
|
|
22989
23556
|
before = before.split("?")[0];
|
|
22990
23557
|
if (!before.startsWith("/"))
|
|
22991
23558
|
return null;
|
|
22992
|
-
return
|
|
23559
|
+
return (0, shared_1.stripTrailingSlashes)(before);
|
|
22993
23560
|
}
|
|
22994
23561
|
function sameHashMount(route, entry) {
|
|
22995
23562
|
const entryMount = hashMount(entry);
|
|
@@ -23045,7 +23612,7 @@ var require_repo_feature_verification = __commonJS({
|
|
|
23045
23612
|
function compileEntryPattern(pattern) {
|
|
23046
23613
|
const trimmed = String(pattern ?? "").trim();
|
|
23047
23614
|
const hashAt = trimmed.indexOf("#/");
|
|
23048
|
-
const raw = (hashAt >= 0 || trimmed.startsWith("#") ? trimmed.slice(Math.max(hashAt, 0)).split("?")[0] : trimmed.split(/[?#]/)[0])
|
|
23615
|
+
const raw = (0, shared_1.stripTrailingSlashes)(hashAt >= 0 || trimmed.startsWith("#") ? trimmed.slice(Math.max(hashAt, 0)).split("?")[0] : trimmed.split(/[?#]/)[0]);
|
|
23049
23616
|
if (!isEntrySpelling(raw) || raw.length > 500)
|
|
23050
23617
|
return null;
|
|
23051
23618
|
let out = "";
|