@validate.qa/runner 1.0.101 → 1.0.102
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/{chunk-IGXGSDE3.mjs → chunk-3JM6XTSZ.mjs} +1298 -384
- package/dist/{chunk-BBTK6OX6.mjs → chunk-4WLP3RWN.mjs} +383 -11
- package/dist/{chunk-XZUUBMP3.mjs → chunk-5DFBEL72.mjs} +1 -1
- package/dist/{chunk-M3VZGHZQ.mjs → chunk-BXLEPT4M.mjs} +20 -10
- package/dist/{chunk-FQUH2PU2.mjs → chunk-CBVHPYVJ.mjs} +62 -2
- package/dist/{chunk-4SZC5ZX3.mjs → chunk-DN3O6EYT.mjs} +19461 -4077
- package/dist/{chunk-SN3PN4G4.mjs → chunk-E2RRAE27.mjs} +6 -2
- package/dist/{chunk-2OKZY3WN.mjs → chunk-E2VEMBQQ.mjs} +2 -2
- package/dist/{chunk-CYWHPXNZ.mjs → chunk-HJ6FKHMM.mjs} +1 -1
- package/dist/{chunk-CO5KHIVX.mjs → chunk-PWFWMVZU.mjs} +1 -1
- package/dist/{chunk-TPU42VH2.mjs → chunk-ZZYBRIBT.mjs} +10 -5
- package/dist/cli.js +135916 -118926
- package/dist/cli.mjs +349 -101
- package/dist/credential-mcp-server.js +1327 -348
- package/dist/credential-mcp-server.mjs +3 -3
- package/dist/discovery-mcp-server.js +1328 -400
- package/dist/discovery-mcp-server.mjs +4 -4
- package/dist/dist-52M5RDZ6.mjs +17 -0
- package/dist/inbox-mcp-server.js +1579 -605
- package/dist/inbox-mcp-server.mjs +3 -3
- package/dist/route-seed-worker.js +1244 -330
- package/dist/route-seed-worker.mjs +4 -4
- package/dist/run-write-guard-preload.js +1622 -332
- package/dist/run-write-guard-preload.mjs +4 -4
- package/dist/run-write-guard.js +1622 -332
- package/dist/run-write-guard.mjs +3 -3
- package/dist/spec-driver.js +1 -1
- package/dist/spec-driver.mjs +1 -1
- package/package.json +3 -2
- package/dist/dist-VTPBY2C4.mjs +0 -17
|
@@ -368,13 +368,13 @@ function isBlockedEnvVar(key) {
|
|
|
368
368
|
function sanitizeTestVariables(vars) {
|
|
369
369
|
if (!vars)
|
|
370
370
|
return {};
|
|
371
|
-
const
|
|
371
|
+
const clean3 = {};
|
|
372
372
|
for (const [key, value] of Object.entries(vars)) {
|
|
373
373
|
if (!isBlockedEnvVar(key)) {
|
|
374
|
-
|
|
374
|
+
clean3[key] = value;
|
|
375
375
|
}
|
|
376
376
|
}
|
|
377
|
-
return
|
|
377
|
+
return clean3;
|
|
378
378
|
}
|
|
379
379
|
function buildSandboxedEnv(processEnv, testVariables) {
|
|
380
380
|
const env = {};
|
|
@@ -1831,9 +1831,40 @@ function semanticTarget(label, platform, base2) {
|
|
|
1831
1831
|
}
|
|
1832
1832
|
};
|
|
1833
1833
|
}
|
|
1834
|
+
function androidResourceOwners(value) {
|
|
1835
|
+
const owners = [];
|
|
1836
|
+
for (const match of value.matchAll(ANDROID_RESOURCE_ID_SUFFIX_RE)) {
|
|
1837
|
+
let runStart = match.index;
|
|
1838
|
+
while (runStart > 0 && /[a-z0-9_.]/i.test(value[runStart - 1]))
|
|
1839
|
+
runStart--;
|
|
1840
|
+
const owner = ownerAtEndOfRun(value.slice(runStart, match.index));
|
|
1841
|
+
if (owner !== null)
|
|
1842
|
+
owners.push(owner);
|
|
1843
|
+
}
|
|
1844
|
+
return owners;
|
|
1845
|
+
}
|
|
1846
|
+
function ownerAtEndOfRun(run) {
|
|
1847
|
+
const segments = run.split(".");
|
|
1848
|
+
const last = segments.length - 1;
|
|
1849
|
+
const startsWithLetter = (segment) => /^[a-z]/i.test(segment);
|
|
1850
|
+
if (last < 1 || !startsWithLetter(segments[last]))
|
|
1851
|
+
return null;
|
|
1852
|
+
let first = last;
|
|
1853
|
+
while (first > 0 && startsWithLetter(segments[first - 1]))
|
|
1854
|
+
first--;
|
|
1855
|
+
let offset = 0;
|
|
1856
|
+
for (let i = 0; i < first - 1; i++)
|
|
1857
|
+
offset += segments[i].length + 1;
|
|
1858
|
+
if (first > 0) {
|
|
1859
|
+
const letterAt = segments[first - 1].search(/[a-z]/i);
|
|
1860
|
+
if (letterAt >= 0)
|
|
1861
|
+
return run.slice(offset + letterAt);
|
|
1862
|
+
offset += segments[first - 1].length + 1;
|
|
1863
|
+
}
|
|
1864
|
+
return first < last ? run.slice(offset) : null;
|
|
1865
|
+
}
|
|
1834
1866
|
function containsForeignAppPackage(value, targetAppId) {
|
|
1835
|
-
for (const
|
|
1836
|
-
const owner = match[1];
|
|
1867
|
+
for (const owner of androidResourceOwners(value)) {
|
|
1837
1868
|
if (targetAppId && owner !== targetAppId && !owner.startsWith(`${targetAppId}.`))
|
|
1838
1869
|
return true;
|
|
1839
1870
|
}
|
|
@@ -1970,7 +2001,7 @@ function healMobileTestSteps(input) {
|
|
|
1970
2001
|
summary: changed ? uniq(summary).slice(0, 12) : []
|
|
1971
2002
|
};
|
|
1972
2003
|
}
|
|
1973
|
-
var MOBILE_STEP_ACTIONS, FOREIGN_PACKAGE_RE,
|
|
2004
|
+
var MOBILE_STEP_ACTIONS, FOREIGN_PACKAGE_RE, ANDROID_RESOURCE_ID_SUFFIX_RE, COMMON_EXTERNAL_PREFIXES;
|
|
1974
2005
|
var init_mobile_step_healer = __esm({
|
|
1975
2006
|
"../shared/dist/mobile-step-healer.js"() {
|
|
1976
2007
|
"use strict";
|
|
@@ -1991,7 +2022,7 @@ var init_mobile_step_healer = __esm({
|
|
|
1991
2022
|
"assertStateDelta"
|
|
1992
2023
|
]);
|
|
1993
2024
|
FOREIGN_PACKAGE_RE = /\b[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*){2,}\b/gi;
|
|
1994
|
-
|
|
2025
|
+
ANDROID_RESOURCE_ID_SUFFIX_RE = /:id\//gi;
|
|
1995
2026
|
COMMON_EXTERNAL_PREFIXES = [
|
|
1996
2027
|
"com.android.",
|
|
1997
2028
|
"com.google.",
|
|
@@ -3701,7 +3732,7 @@ var init_assertion_classifier = __esm({
|
|
|
3701
3732
|
// toHaveCount(0) stays tier 4 (absence proof) via TIER4_PATTERNS.
|
|
3702
3733
|
[/\.toHaveCount\s*\(\s*[1-9]\d*\s*\)/, "non-zero toHaveCount asserts a record count that changes between runs"],
|
|
3703
3734
|
// parenthesised counts like "(3)" or "[12]" in text - but not Playwright method args
|
|
3704
|
-
[
|
|
3735
|
+
[/\((?<!\.\w{3,20}\s*\()\s*\d+\s*\)(?!\s*(?:[,;]\s*)?\))/i, "parenthesised count is session-specific"],
|
|
3705
3736
|
// bare email addresses in assertion content
|
|
3706
3737
|
[/\b[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}\b/i, "email address is user-specific"],
|
|
3707
3738
|
// currency/balance amounts (dynamic account data)
|
|
@@ -3947,7 +3978,7 @@ var init_assertion_proof = __esm({
|
|
|
3947
3978
|
PRESENCE_MATCHER = /\.(?:toBeVisible|toBeAttached|toBeEnabled|toBeInViewport|toBeEditable|toBeFocused)\s*\(|\.toHaveCount\s*\(\s*1\s*\)/;
|
|
3948
3979
|
TEXT_MATCHER = /\.(?:toHaveText|toContainText)\s*\(/;
|
|
3949
3980
|
ANY_VALUE_SOURCE = /^(?:\.|\\S|\\w|\\d|\[\^\\s\]|\[0-9\]|\[\\d\]|\[\\w\]|\[\\s\\S\])[+*]?\??$/;
|
|
3950
|
-
ANY_VALUE_MATCHER = /\.(?:toHaveValue|toHaveText|toContainText)\s*\(\s*\/((?:\\.|[^/\\]){1,300})\/[a-z]*\s
|
|
3981
|
+
ANY_VALUE_MATCHER = /\.(?:toHaveValue|toHaveText|toContainText)\s*\(\s*\/((?:\\.|[^/\\]){1,300})\/[a-z]*\s*[,)]/;
|
|
3951
3982
|
EMPTY_NOT_VALUE = /\.not\s*\.\s*toHaveValue\s*\(\s*(?:['"`]{2}|\/\^?\$?\/)/;
|
|
3952
3983
|
TEXT_MATCHER_LITERAL = /\.(?:toHaveText|toContainText)\s*\(\s*(['"`])((?:(?!\1).){1,300})\1/;
|
|
3953
3984
|
GET_BY_TEXT_LITERAL = /getByText\s*\(\s*(['"`])((?:(?!\1).){1,300})\1/;
|
|
@@ -3964,7 +3995,7 @@ var init_assertion_proof = __esm({
|
|
|
3964
3995
|
DATA_ROLE = /getByRole\s*\(\s*['"`](?:dialog|alertdialog|alert|cell|gridcell|row|rowheader|columnheader|listitem|article|option|status|note|tooltip|paragraph|term|definition|log|marquee|timer|meter)['"`]/gi;
|
|
3965
3996
|
TEXT_LOCATOR = /\.getByText\s*\(|\.filter\s*\(\s*\{\s*hasText\s*:/g;
|
|
3966
3997
|
CSS_LOCATOR = /\.locator\s*\(\s*(['"`])((?:(?!\1).){1,300})\1/g;
|
|
3967
|
-
SPECIFIC_CSS = /\[[^\]]*=|#[\w-]|has-text|:text/;
|
|
3998
|
+
SPECIFIC_CSS = /\[[^\][=]*=|#[\w-]|has-text|:text/;
|
|
3968
3999
|
}
|
|
3969
4000
|
});
|
|
3970
4001
|
|
|
@@ -4928,22 +4959,37 @@ function classifyAuthHandoffIntent(text) {
|
|
|
4928
4959
|
function withoutIdpUrlAsserts(code) {
|
|
4929
4960
|
return code.replace(/\.(?:toHaveURL|waitForURL)\s*\((?:[^)(]|\([^)]*\))*\)/g, (match) => /accountchooser|accounts\.google|appleid\.apple|login\.microsoftonline|\/v\d+\/signin|oauth/i.test(match) ? "" : match);
|
|
4930
4961
|
}
|
|
4962
|
+
function stripLineComment(line) {
|
|
4963
|
+
let from = line.length;
|
|
4964
|
+
while (from > 0) {
|
|
4965
|
+
const c = line.charCodeAt(from - 1);
|
|
4966
|
+
if (c === 10 || c === 13 || c === 8232 || c === 8233)
|
|
4967
|
+
break;
|
|
4968
|
+
from--;
|
|
4969
|
+
}
|
|
4970
|
+
const at = line.indexOf("//", from);
|
|
4971
|
+
return at < 0 ? line : line.slice(0, at);
|
|
4972
|
+
}
|
|
4931
4973
|
function stripComments(code) {
|
|
4932
|
-
return replaceBlockComments(code, () => "").split("\n").map(
|
|
4974
|
+
return replaceBlockComments(code, () => "").split("\n").map(stripLineComment).join("\n");
|
|
4933
4975
|
}
|
|
4934
|
-
function
|
|
4935
|
-
const
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
let depth = 0;
|
|
4939
|
-
for (let i = open; i < code.length; i++) {
|
|
4976
|
+
function closingParens(code) {
|
|
4977
|
+
const close = new Int32Array(code.length).fill(-1);
|
|
4978
|
+
const open = [];
|
|
4979
|
+
for (let i = 0; i < code.length; i++) {
|
|
4940
4980
|
const c = code[i];
|
|
4941
4981
|
if (c === "(")
|
|
4942
|
-
|
|
4943
|
-
else if (c === ")" &&
|
|
4944
|
-
|
|
4982
|
+
open.push(i);
|
|
4983
|
+
else if (c === ")" && open.length > 0)
|
|
4984
|
+
close[open.pop()] = i;
|
|
4945
4985
|
}
|
|
4946
|
-
return
|
|
4986
|
+
return close;
|
|
4987
|
+
}
|
|
4988
|
+
function balancedCall(code, close, start) {
|
|
4989
|
+
const open = code.indexOf("(", start);
|
|
4990
|
+
if (open < 0 || close[open] < 0)
|
|
4991
|
+
return null;
|
|
4992
|
+
return { inner: code.slice(open + 1, close[open]), end: close[open] };
|
|
4947
4993
|
}
|
|
4948
4994
|
function resolveLocator(code, subject) {
|
|
4949
4995
|
const id = subject.trim().match(/^([A-Za-z_$][\w$]*)$/);
|
|
@@ -4991,8 +5037,10 @@ function isAuthHandoffDismissCode(code) {
|
|
|
4991
5037
|
const expects = [];
|
|
4992
5038
|
const re = new RegExp(EXPECT_CALL_RE.source, "g");
|
|
4993
5039
|
let match;
|
|
5040
|
+
let close = null;
|
|
4994
5041
|
while (match = re.exec(body)) {
|
|
4995
|
-
|
|
5042
|
+
close ??= closingParens(body);
|
|
5043
|
+
const found = balancedCall(body, close, match.index);
|
|
4996
5044
|
if (found)
|
|
4997
5045
|
expects.push({ inner: found.inner, tail: body.slice(found.end, found.end + 48) });
|
|
4998
5046
|
}
|
|
@@ -5037,12 +5085,209 @@ var init_auth_handoff_coverage = __esm({
|
|
|
5037
5085
|
}
|
|
5038
5086
|
});
|
|
5039
5087
|
|
|
5088
|
+
// ../shared/dist/auth-plan.js
|
|
5089
|
+
function hasInlineLoginSteps(playwrightCode) {
|
|
5090
|
+
if (!playwrightCode)
|
|
5091
|
+
return false;
|
|
5092
|
+
return /process\.env\.TEST_USER_EMAIL!?/.test(playwrightCode) && /process\.env\.TEST_USER_PASSWORD!?/.test(playwrightCode);
|
|
5093
|
+
}
|
|
5094
|
+
function hasGeneratedAuthGuard(playwrightCode) {
|
|
5095
|
+
if (!playwrightCode)
|
|
5096
|
+
return false;
|
|
5097
|
+
return /\/\/ >>> VALIDATE_QA_AUTH v(\d+)\b[\s\S]*?process\.env\.AUTH_FILE[\s\S]*?\/\/ <<< VALIDATE_QA_AUTH v\1\b/.test(playwrightCode);
|
|
5098
|
+
}
|
|
5099
|
+
function legacyNeedsCookieState(tags, code, sharedAuthInjectEnabled) {
|
|
5100
|
+
if (!sharedAuthInjectEnabled)
|
|
5101
|
+
return false;
|
|
5102
|
+
if (tags.includes("@auth"))
|
|
5103
|
+
return false;
|
|
5104
|
+
if (tags.includes("@api"))
|
|
5105
|
+
return true;
|
|
5106
|
+
if (!tags.includes("@needs-auth"))
|
|
5107
|
+
return false;
|
|
5108
|
+
if (sharedAuthInjectEnabled && tags.includes("@auth-guarded") && hasGeneratedAuthGuard(code))
|
|
5109
|
+
return true;
|
|
5110
|
+
const isGeneratedVariant = tags.includes("@edge") || tags.includes("@bug");
|
|
5111
|
+
const hasLegacyInlineLogin = tags.includes("@discovered") && !isGeneratedVariant && hasInlineLoginSteps(code);
|
|
5112
|
+
return !tags.includes("@inline-login") && !hasLegacyInlineLogin;
|
|
5113
|
+
}
|
|
5114
|
+
function codeReadsStoredCredentials(code) {
|
|
5115
|
+
if (!code)
|
|
5116
|
+
return false;
|
|
5117
|
+
if (/__vqaLogin\b/.test(code))
|
|
5118
|
+
return true;
|
|
5119
|
+
for (const m of code.matchAll(/TEST_[A-Z0-9_]+/g)) {
|
|
5120
|
+
if (/_(?:EMAIL|PASSWORD)/.test(m[0]))
|
|
5121
|
+
return true;
|
|
5122
|
+
}
|
|
5123
|
+
for (const m of code.matchAll(/process\.env\s*\[([^\]]*)\]?/g)) {
|
|
5124
|
+
if (!/^\s*(['"])[^'"`]*\1\s*$/.test(m[1]))
|
|
5125
|
+
return true;
|
|
5126
|
+
}
|
|
5127
|
+
return false;
|
|
5128
|
+
}
|
|
5129
|
+
function explicitAuthTypeTags(t) {
|
|
5130
|
+
const found = /* @__PURE__ */ new Set();
|
|
5131
|
+
for (const tag of t) {
|
|
5132
|
+
if (AUTH_TYPE_TAGS.includes(tag))
|
|
5133
|
+
found.add(tag);
|
|
5134
|
+
else if (Object.hasOwn(AUTH_TAG_ALIASES, tag))
|
|
5135
|
+
found.add(AUTH_TAG_ALIASES[tag]);
|
|
5136
|
+
}
|
|
5137
|
+
return [...found];
|
|
5138
|
+
}
|
|
5139
|
+
function invalidPlan(configurationError) {
|
|
5140
|
+
return {
|
|
5141
|
+
version: 1,
|
|
5142
|
+
source: "invalid",
|
|
5143
|
+
acquisition: "unsupported",
|
|
5144
|
+
presentation: "none",
|
|
5145
|
+
needsCookieState: false,
|
|
5146
|
+
needsStandingHeaders: false,
|
|
5147
|
+
standingHeadersMode: "none",
|
|
5148
|
+
needsLoginCreds: false,
|
|
5149
|
+
requiredEnvKeys: [],
|
|
5150
|
+
configurationError
|
|
5151
|
+
};
|
|
5152
|
+
}
|
|
5153
|
+
function explicitPlan(authTypeTag2, acquisition, presentation, opts) {
|
|
5154
|
+
return {
|
|
5155
|
+
version: 1,
|
|
5156
|
+
source: "explicit",
|
|
5157
|
+
authTypeTag: authTypeTag2,
|
|
5158
|
+
acquisition,
|
|
5159
|
+
presentation,
|
|
5160
|
+
needsCookieState: opts.cookie,
|
|
5161
|
+
needsStandingHeaders: opts.standingMode === "required",
|
|
5162
|
+
standingHeadersMode: opts.standingMode,
|
|
5163
|
+
needsLoginCreds: TEST_USER_ENV_KEYS.every((k) => opts.requiredEnvKeys.includes(k)),
|
|
5164
|
+
requiredEnvKeys: opts.requiredEnvKeys
|
|
5165
|
+
};
|
|
5166
|
+
}
|
|
5167
|
+
function deriveAuthPlan(tags, code, opts) {
|
|
5168
|
+
const t = Array.isArray(tags) ? tags : [];
|
|
5169
|
+
const sharedFlag = opts?.sharedAuthInjectEnabled ?? false;
|
|
5170
|
+
const isApi = t.includes("@api");
|
|
5171
|
+
const explicit = isApi ? explicitAuthTypeTags(t) : [];
|
|
5172
|
+
if (explicit.length > 0) {
|
|
5173
|
+
if (t.includes("@auth")) {
|
|
5174
|
+
return invalidPlan("@auth (self-login UI) cannot be combined with an explicit @api auth-type tag");
|
|
5175
|
+
}
|
|
5176
|
+
const loginCompose = explicit.filter((e) => e === AUTH_TAG_COOKIE_STATE || e === AUTH_TAG_LOGIN_COOKIE || e === AUTH_TAG_LOGIN_BEARER);
|
|
5177
|
+
if (explicit.length === 2 && explicit.includes(AUTH_TAG_STANDING) && loginCompose.length === 1) {
|
|
5178
|
+
const composedKeys = [...TEST_USER_ENV_KEYS, API_AUTH_HEADERS_ENV_KEY];
|
|
5179
|
+
switch (loginCompose[0]) {
|
|
5180
|
+
case AUTH_TAG_COOKIE_STATE:
|
|
5181
|
+
return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
5182
|
+
case AUTH_TAG_LOGIN_COOKIE:
|
|
5183
|
+
return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
5184
|
+
case AUTH_TAG_LOGIN_BEARER:
|
|
5185
|
+
return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5188
|
+
if (explicit.length > 1) {
|
|
5189
|
+
return invalidPlan(`conflicting auth-type tags: ${explicit.join(", ")}`);
|
|
5190
|
+
}
|
|
5191
|
+
switch (explicit[0]) {
|
|
5192
|
+
case AUTH_TAG_UNSUPPORTED:
|
|
5193
|
+
return {
|
|
5194
|
+
...invalidPlan("auth scheme not yet drivable (e.g. HMAC / mTLS / provider-replay) \u2014 needs manual configuration")
|
|
5195
|
+
};
|
|
5196
|
+
case AUTH_TAG_NONE:
|
|
5197
|
+
return explicitPlan(AUTH_TAG_NONE, "none", "none", { cookie: false, standingMode: "none", requiredEnvKeys: [] });
|
|
5198
|
+
case AUTH_TAG_STANDING:
|
|
5199
|
+
return explicitPlan(AUTH_TAG_STANDING, "standing-secret", "standing-header", { cookie: false, standingMode: "required", requiredEnvKeys: [API_AUTH_HEADERS_ENV_KEY] });
|
|
5200
|
+
case AUTH_TAG_LOGIN_BEARER:
|
|
5201
|
+
return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "none", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
5202
|
+
case AUTH_TAG_LOGIN_COOKIE:
|
|
5203
|
+
return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
5204
|
+
case AUTH_TAG_COOKIE_STATE:
|
|
5205
|
+
return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
5208
|
+
const cookie = legacyNeedsCookieState(t, code, sharedFlag);
|
|
5209
|
+
const authedLegacy = t.includes("@auth") || t.includes("@needs-auth") || isApi;
|
|
5210
|
+
const selfLoginWithoutStoredCreds = t.includes("@auth") && !t.includes("@needs-auth") && !isApi && !!code?.trim() && !/TEST_USER_EMAIL|TEST_USER_PASSWORD|__vqaLogin\b/.test(code);
|
|
5211
|
+
const apiWithoutStoredCreds = isApi && !t.includes("@auth") && !t.includes("@needs-auth") && !!code?.trim() && !codeReadsStoredCredentials(code);
|
|
5212
|
+
const needsLoginCreds = authedLegacy && !selfLoginWithoutStoredCreds && !apiWithoutStoredCreds;
|
|
5213
|
+
return {
|
|
5214
|
+
version: 1,
|
|
5215
|
+
source: "legacy",
|
|
5216
|
+
acquisition: cookie ? "browser-bootstrap" : authedLegacy ? "request-login" : "none",
|
|
5217
|
+
presentation: authedLegacy ? "cookie" : "none",
|
|
5218
|
+
needsCookieState: cookie,
|
|
5219
|
+
needsStandingHeaders: false,
|
|
5220
|
+
// A legacy bare-@api test keeps injecting standing headers IF the project configured them
|
|
5221
|
+
// (preserving today's behavior) — but does not require them. Non-@api legacy tests never inject.
|
|
5222
|
+
standingHeadersMode: isApi ? "legacy-if-configured" : "none",
|
|
5223
|
+
needsLoginCreds,
|
|
5224
|
+
requiredEnvKeys: needsLoginCreds ? [...TEST_USER_ENV_KEYS] : []
|
|
5225
|
+
};
|
|
5226
|
+
}
|
|
5227
|
+
function authTypeTag(tags) {
|
|
5228
|
+
const t = Array.isArray(tags) ? tags : [];
|
|
5229
|
+
const explicit = explicitAuthTypeTags(t);
|
|
5230
|
+
return explicit.length === 1 ? explicit[0] : void 0;
|
|
5231
|
+
}
|
|
5232
|
+
var AUTH_TAG_COOKIE_STATE, AUTH_TAG_LOGIN_COOKIE, AUTH_TAG_LOGIN_BEARER, AUTH_TAG_STANDING, AUTH_TAG_NONE, AUTH_TAG_UNSUPPORTED, AUTH_TYPE_TAGS, AUTH_TAG_ALIASES, TEST_USER_ENV_KEYS, API_AUTH_HEADERS_ENV_KEY;
|
|
5233
|
+
var init_auth_plan = __esm({
|
|
5234
|
+
"../shared/dist/auth-plan.js"() {
|
|
5235
|
+
"use strict";
|
|
5236
|
+
AUTH_TAG_COOKIE_STATE = "@auth-cookie-state";
|
|
5237
|
+
AUTH_TAG_LOGIN_COOKIE = "@auth-login-cookie";
|
|
5238
|
+
AUTH_TAG_LOGIN_BEARER = "@auth-login-bearer";
|
|
5239
|
+
AUTH_TAG_STANDING = "@auth-standing";
|
|
5240
|
+
AUTH_TAG_NONE = "@auth-none";
|
|
5241
|
+
AUTH_TAG_UNSUPPORTED = "@auth-unsupported";
|
|
5242
|
+
AUTH_TYPE_TAGS = [
|
|
5243
|
+
AUTH_TAG_COOKIE_STATE,
|
|
5244
|
+
AUTH_TAG_LOGIN_COOKIE,
|
|
5245
|
+
AUTH_TAG_LOGIN_BEARER,
|
|
5246
|
+
AUTH_TAG_STANDING,
|
|
5247
|
+
AUTH_TAG_NONE,
|
|
5248
|
+
AUTH_TAG_UNSUPPORTED
|
|
5249
|
+
];
|
|
5250
|
+
AUTH_TAG_ALIASES = {
|
|
5251
|
+
"@auth-cookie": AUTH_TAG_COOKIE_STATE,
|
|
5252
|
+
"@auth-bearer": AUTH_TAG_LOGIN_BEARER,
|
|
5253
|
+
"@auth-token": AUTH_TAG_LOGIN_BEARER,
|
|
5254
|
+
"@auth-api-key": AUTH_TAG_STANDING,
|
|
5255
|
+
"@auth-standing-header": AUTH_TAG_STANDING
|
|
5256
|
+
};
|
|
5257
|
+
TEST_USER_ENV_KEYS = ["TEST_USER_EMAIL", "TEST_USER_PASSWORD"];
|
|
5258
|
+
API_AUTH_HEADERS_ENV_KEY = "API_AUTH_HEADERS";
|
|
5259
|
+
}
|
|
5260
|
+
});
|
|
5261
|
+
|
|
5040
5262
|
// ../shared/dist/assertion-strength.js
|
|
5263
|
+
function parseLastActionProofMode(raw) {
|
|
5264
|
+
const v = (raw ?? "").trim().toLowerCase();
|
|
5265
|
+
return v === "on" || v === "shadow" ? v : "off";
|
|
5266
|
+
}
|
|
5267
|
+
function isUrlProof(stmt) {
|
|
5268
|
+
if (URL_PROOF.test(stmt))
|
|
5269
|
+
return true;
|
|
5270
|
+
let calls = 0;
|
|
5271
|
+
for (const open of stmt.matchAll(POLL_OPEN)) {
|
|
5272
|
+
if (++calls > POLL_CALLS_MAX)
|
|
5273
|
+
break;
|
|
5274
|
+
const from = open.index + open[0].length;
|
|
5275
|
+
const args = balancedArguments(stmt, from, MATCHER_ARGS_MAX);
|
|
5276
|
+
if (args === null)
|
|
5277
|
+
continue;
|
|
5278
|
+
const subject = pollSubject(args);
|
|
5279
|
+
if (!POLL_PAGE_URL.test(subject))
|
|
5280
|
+
continue;
|
|
5281
|
+
if (!POLL_PAGE_URL_ONLY.test(subject) || PAGE_VALUE_MATCHER.test(stmt.slice(from + args.length)))
|
|
5282
|
+
return true;
|
|
5283
|
+
}
|
|
5284
|
+
return false;
|
|
5285
|
+
}
|
|
5041
5286
|
function liveExpectStatements(uncommented) {
|
|
5042
5287
|
const masked = maskedOutsideStrings(uncommented).split("\n");
|
|
5043
5288
|
return accumulateStatements(uncommented.split("\n")).filter((s) => EXPECT_STMT.test(s.stmt) && EXPECT_STMT.test(masked[s.startLine - 1] ?? ""));
|
|
5044
5289
|
}
|
|
5045
|
-
function
|
|
5290
|
+
function stripLineComment2(ln) {
|
|
5046
5291
|
let quote = null;
|
|
5047
5292
|
for (let i = 0; i < ln.length; i++) {
|
|
5048
5293
|
const c = ln[i];
|
|
@@ -5065,7 +5310,7 @@ function splitInteractionStatementsWithLines(lines) {
|
|
|
5065
5310
|
let depth = 0;
|
|
5066
5311
|
let scaffold = false;
|
|
5067
5312
|
for (let i = 0; i < lines.length; i++) {
|
|
5068
|
-
const ln =
|
|
5313
|
+
const ln = stripLineComment2(lines[i]).trim();
|
|
5069
5314
|
if (!ln)
|
|
5070
5315
|
continue;
|
|
5071
5316
|
const isContinuation = out.length > 0 && (ln.startsWith(".") || depth > 0 && !scaffold);
|
|
@@ -5089,7 +5334,7 @@ function testBlockIds(lines) {
|
|
|
5089
5334
|
let any = false;
|
|
5090
5335
|
let block = 0;
|
|
5091
5336
|
for (let i = 0; i < lines.length; i++) {
|
|
5092
|
-
const ln =
|
|
5337
|
+
const ln = stripLineComment2(lines[i]).trim();
|
|
5093
5338
|
if (!open) {
|
|
5094
5339
|
if (!TEST_BLOCK_OPEN.test(ln))
|
|
5095
5340
|
continue;
|
|
@@ -5173,7 +5418,7 @@ function expectSubjectSpan(stmt) {
|
|
|
5173
5418
|
}
|
|
5174
5419
|
return null;
|
|
5175
5420
|
}
|
|
5176
|
-
function resolveSubjectBindings(stmts, bindings, blockOfLine) {
|
|
5421
|
+
function resolveSubjectBindings(stmts, bindings, blockOfLine, boundAt) {
|
|
5177
5422
|
if (bindings.length === 0)
|
|
5178
5423
|
return [...stmts];
|
|
5179
5424
|
return stmts.map((item) => {
|
|
@@ -5196,14 +5441,22 @@ function resolveSubjectBindings(stmts, bindings, blockOfLine) {
|
|
|
5196
5441
|
subject = `${lead[1]}${found.expr.replace(/;\s*$/, "")}${subject.slice(lead[0].length)}`;
|
|
5197
5442
|
line = found.line;
|
|
5198
5443
|
}
|
|
5199
|
-
|
|
5444
|
+
const out = { ...item, stmt: `${item.stmt.slice(0, span.from)}${subject}${item.stmt.slice(span.to)}` };
|
|
5445
|
+
if (boundAt !== void 0 && line !== item.startLine)
|
|
5446
|
+
boundAt.set(out, line);
|
|
5447
|
+
return out;
|
|
5200
5448
|
});
|
|
5201
5449
|
}
|
|
5202
5450
|
function stripLoginScaffolding(code) {
|
|
5203
|
-
const
|
|
5451
|
+
const lines = code.split("\n");
|
|
5452
|
+
return keptScaffoldingLines(lines).map((i) => lines[i]).join("\n");
|
|
5453
|
+
}
|
|
5454
|
+
function keptScaffoldingLines(lines) {
|
|
5455
|
+
const kept = [];
|
|
5204
5456
|
let inAuth = false;
|
|
5205
5457
|
let inInbox = false;
|
|
5206
|
-
for (
|
|
5458
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5459
|
+
const ln = lines[i];
|
|
5207
5460
|
if (!inAuth && !inInbox && AUTH_START.test(ln)) {
|
|
5208
5461
|
inAuth = true;
|
|
5209
5462
|
continue;
|
|
@@ -5224,9 +5477,9 @@ function stripLoginScaffolding(code) {
|
|
|
5224
5477
|
}
|
|
5225
5478
|
if (LOGIN_CALL_LINE.test(ln))
|
|
5226
5479
|
continue;
|
|
5227
|
-
|
|
5480
|
+
kept.push(i);
|
|
5228
5481
|
}
|
|
5229
|
-
return
|
|
5482
|
+
return kept;
|
|
5230
5483
|
}
|
|
5231
5484
|
function stripCommentsAndLogs(code) {
|
|
5232
5485
|
return stripCoverageComments(code).split("\n").filter((ln) => !/^\s*console\./.test(ln)).join("\n");
|
|
@@ -5378,6 +5631,41 @@ function isVacuousErrorAbsence(stmt, presence) {
|
|
|
5378
5631
|
return false;
|
|
5379
5632
|
return !presence().includes(m[1]);
|
|
5380
5633
|
}
|
|
5634
|
+
function isVacuousAccountAbsence(stmt, shown, account) {
|
|
5635
|
+
if (!claimsAbsence(stmt))
|
|
5636
|
+
return false;
|
|
5637
|
+
if (!ABSENCE_MATCHER.test(stmt)) {
|
|
5638
|
+
const negated = NEGATED_TEXT.exec(stmt);
|
|
5639
|
+
if (!negated)
|
|
5640
|
+
return false;
|
|
5641
|
+
const read = account.exec(stmt.slice(negated.index));
|
|
5642
|
+
return read !== null && !shown().has(read[0]);
|
|
5643
|
+
}
|
|
5644
|
+
const m = account.exec(stmt);
|
|
5645
|
+
if (!m || shown().has(m[0]))
|
|
5646
|
+
return false;
|
|
5647
|
+
for (let at = endOfEnclosingCall(stmt, m.index + m[0].length); at < stmt.length; at = endOfEnclosingCall(stmt, at)) {
|
|
5648
|
+
SCOPING_STEP.lastIndex = at;
|
|
5649
|
+
if (SCOPING_STEP.test(stmt))
|
|
5650
|
+
return false;
|
|
5651
|
+
}
|
|
5652
|
+
return true;
|
|
5653
|
+
}
|
|
5654
|
+
function endOfEnclosingCall(stmt, from) {
|
|
5655
|
+
let depth = 0;
|
|
5656
|
+
for (let i = from; i < stmt.length; i++) {
|
|
5657
|
+
const c = stmt[i];
|
|
5658
|
+
if (c === "'" || c === '"') {
|
|
5659
|
+
for (i++; i < stmt.length && stmt[i] !== c; i++)
|
|
5660
|
+
if (stmt[i] === "\\")
|
|
5661
|
+
i++;
|
|
5662
|
+
} else if (c === "(")
|
|
5663
|
+
depth++;
|
|
5664
|
+
else if (c === ")" && depth-- === 0)
|
|
5665
|
+
return i + 1;
|
|
5666
|
+
}
|
|
5667
|
+
return stmt.length;
|
|
5668
|
+
}
|
|
5381
5669
|
function isBareContainerVisibility(stmt) {
|
|
5382
5670
|
return BARE_CONTAINER_ROLE.test(stmt) && !GET_BY_TEXT.test(stmt) && !HAS_TEXT_FILTER.test(stmt) && !TEXT_CONTENT_MATCHER.test(stmt);
|
|
5383
5671
|
}
|
|
@@ -5459,7 +5747,7 @@ function roleNames(s) {
|
|
|
5459
5747
|
function interactedControlNames(lines) {
|
|
5460
5748
|
const names = /* @__PURE__ */ new Set();
|
|
5461
5749
|
for (const raw of lines) {
|
|
5462
|
-
const ln =
|
|
5750
|
+
const ln = stripLineComment2(raw);
|
|
5463
5751
|
if (!INTERACT_VERB.test(ln))
|
|
5464
5752
|
continue;
|
|
5465
5753
|
for (const n of roleNames(ln))
|
|
@@ -5655,6 +5943,14 @@ function balancedArguments(code, from, limit = code.length) {
|
|
|
5655
5943
|
}
|
|
5656
5944
|
return null;
|
|
5657
5945
|
}
|
|
5946
|
+
function expectCallTail(stmt) {
|
|
5947
|
+
const at = stmt.search(EXPECT_CALL_RE);
|
|
5948
|
+
if (at < 0)
|
|
5949
|
+
return null;
|
|
5950
|
+
const from = stmt.indexOf("(", at) + 1;
|
|
5951
|
+
const args = balancedArguments(stmt, from, MATCHER_ARGS_MAX);
|
|
5952
|
+
return args === null ? null : stmt.slice(from + args.length).trimStart();
|
|
5953
|
+
}
|
|
5658
5954
|
function matcherArgumentsAreConstant(body, from) {
|
|
5659
5955
|
MATCHER_OPEN.lastIndex = from;
|
|
5660
5956
|
const opened = MATCHER_OPEN.exec(body);
|
|
@@ -5696,9 +5992,12 @@ function envValueKey(name) {
|
|
|
5696
5992
|
function enteredValues(stmts) {
|
|
5697
5993
|
const values = /* @__PURE__ */ new Map();
|
|
5698
5994
|
const navLines = [];
|
|
5995
|
+
const accountConsts = /* @__PURE__ */ new Set();
|
|
5699
5996
|
for (const { stmt, startLine } of stmts) {
|
|
5700
5997
|
if (PAGE_NAV.test(stmt))
|
|
5701
5998
|
navLines.push(startLine);
|
|
5999
|
+
for (const c of stmt.matchAll(ACCOUNT_CONST))
|
|
6000
|
+
accountConsts.add(c[1].replace(/\$/g, "\\$"));
|
|
5702
6001
|
let m;
|
|
5703
6002
|
ENTERED_VALUE.lastIndex = 0;
|
|
5704
6003
|
const add = (value) => {
|
|
@@ -5714,7 +6013,8 @@ function enteredValues(stmts) {
|
|
|
5714
6013
|
while (m = ENTERED_ENV.exec(stmt))
|
|
5715
6014
|
add(envValueKey(m[1] ?? m[2] ?? m[3] ?? m[4]));
|
|
5716
6015
|
}
|
|
5717
|
-
|
|
6016
|
+
const consts = accountConsts.size > 0 ? `|(?<![\\w$.])(?:${[...accountConsts].join("|")})(?![\\w$])` : "";
|
|
6017
|
+
return { values, navLines, account: new RegExp(`${ACCOUNT_ENV}${consts}`) };
|
|
5718
6018
|
}
|
|
5719
6019
|
function countBelow(lines, line) {
|
|
5720
6020
|
let lo = 0;
|
|
@@ -5851,6 +6151,9 @@ function comparesWithItself(subject, tail) {
|
|
|
5851
6151
|
return expected === squash2(m[1] === "toHaveCount" ? `${subject}.count()` : subject);
|
|
5852
6152
|
}
|
|
5853
6153
|
function asPlainAssertion(s) {
|
|
6154
|
+
return plainAssertion(s, false);
|
|
6155
|
+
}
|
|
6156
|
+
function plainAssertion(s, keepSelfCompare) {
|
|
5854
6157
|
const { stmt } = s;
|
|
5855
6158
|
if (COUNT_AT_LEAST_ZERO.test(stmt))
|
|
5856
6159
|
return [];
|
|
@@ -5859,13 +6162,13 @@ function asPlainAssertion(s) {
|
|
|
5859
6162
|
return [s];
|
|
5860
6163
|
const plain = subject.replace(/^await\s+/, "").trim();
|
|
5861
6164
|
const subjectAt = stmt.indexOf(subject, stmt.search(EXPECT_CALL_RE));
|
|
5862
|
-
const tail = stmt.slice(subjectAt + subject.length).trimStart();
|
|
6165
|
+
const tail = (stmt.startsWith("expect.poll", stmt.search(EXPECT_CALL_RE)) ? expectCallTail(stmt) : null) ?? stmt.slice(subjectAt + subject.length).trimStart();
|
|
5863
6166
|
const as = (rewritten) => [{ ...s, stmt: rewritten }];
|
|
5864
6167
|
if (LOCATOR_EXPR.test(plain) && !LOCATOR_VALUE_READ.test(plain) && ALWAYS_TRUE_ON_OBJECT.test(tail))
|
|
5865
6168
|
return [];
|
|
5866
6169
|
if (DEFINED_VALUE_READ.test(plain) && ALWAYS_DEFINED.test(tail))
|
|
5867
6170
|
return [];
|
|
5868
|
-
if (comparesWithItself(plain, tail))
|
|
6171
|
+
if (!keepSelfCompare && comparesWithItself(plain, tail))
|
|
5869
6172
|
return [];
|
|
5870
6173
|
const visible = /^(.+)\.\s*isVisible\s*\(\s*\)$/s.exec(plain);
|
|
5871
6174
|
if (visible && IS_VISIBLE_TRUE.test(tail))
|
|
@@ -5876,6 +6179,9 @@ function asPlainAssertion(s) {
|
|
|
5876
6179
|
}
|
|
5877
6180
|
return [s];
|
|
5878
6181
|
}
|
|
6182
|
+
function assertionStrengthHoistedReadsOn(value = typeof process !== "undefined" ? process.env?.ASSERTION_STRENGTH_HOISTED_READS : void 0) {
|
|
6183
|
+
return /^(1|true|on)$/i.test((value ?? "").trim());
|
|
6184
|
+
}
|
|
5879
6185
|
function creditsOutcomeAlways(stmt, entered, startLine) {
|
|
5880
6186
|
if (!OUTCOME_ALWAYS.some((re) => re.test(stmt)))
|
|
5881
6187
|
return false;
|
|
@@ -5908,6 +6214,22 @@ function successNoticeQuote(stmt) {
|
|
|
5908
6214
|
}
|
|
5909
6215
|
return null;
|
|
5910
6216
|
}
|
|
6217
|
+
function namedPagePath(arg) {
|
|
6218
|
+
const text = arg.trim();
|
|
6219
|
+
let path;
|
|
6220
|
+
const quoted = /^(['"`])([^'"`]*)\1$/.exec(text);
|
|
6221
|
+
const regex = quoted ? null : /^\/((?:\\.|[^/\\])+)\/[a-z]*$/.exec(text);
|
|
6222
|
+
if (quoted) {
|
|
6223
|
+
path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\*\*$/, "");
|
|
6224
|
+
} else if (regex) {
|
|
6225
|
+
path = regex[1].replace(/^\^/, "").replace(/^\.\*/, "").replace(/\$$/, "").replace(/\\([/.-])/g, "$1");
|
|
6226
|
+
} else {
|
|
6227
|
+
return null;
|
|
6228
|
+
}
|
|
6229
|
+
if (!PLAIN_PAGE_PATH.test(path))
|
|
6230
|
+
return null;
|
|
6231
|
+
return path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
6232
|
+
}
|
|
5911
6233
|
function provesStoredControlValue(stmt, startLine, persistence) {
|
|
5912
6234
|
if (!persistence || startLine === void 0)
|
|
5913
6235
|
return false;
|
|
@@ -5934,7 +6256,64 @@ function reopenedAfterSave(startLine, persistence, reopens = persistence.reloadL
|
|
|
5934
6256
|
}
|
|
5935
6257
|
return lo < reopens.length && reopens[lo] < startLine;
|
|
5936
6258
|
}
|
|
5937
|
-
function
|
|
6259
|
+
function actsBetween(withLines, from, to) {
|
|
6260
|
+
let acts = actLinesOf.get(withLines);
|
|
6261
|
+
if (acts === void 0) {
|
|
6262
|
+
acts = withLines.filter((s) => ACTS.test(s.stmt)).map((s) => s.startLine).sort((a, b) => a - b);
|
|
6263
|
+
actLinesOf.set(withLines, acts);
|
|
6264
|
+
}
|
|
6265
|
+
let lo = 0;
|
|
6266
|
+
let hi = acts.length;
|
|
6267
|
+
while (lo < hi) {
|
|
6268
|
+
const mid = lo + hi >> 1;
|
|
6269
|
+
if (acts[mid] <= from)
|
|
6270
|
+
lo = mid + 1;
|
|
6271
|
+
else
|
|
6272
|
+
hi = mid;
|
|
6273
|
+
}
|
|
6274
|
+
return lo < acts.length && acts[lo] < to;
|
|
6275
|
+
}
|
|
6276
|
+
function signInProofWindow(stmts, withLines, lines, blockIds) {
|
|
6277
|
+
if (blockIds === null)
|
|
6278
|
+
return void 0;
|
|
6279
|
+
const testBodies2 = /* @__PURE__ */ new Set();
|
|
6280
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6281
|
+
const id = blockIds[i];
|
|
6282
|
+
if (id > 0 && (i === 0 || blockIds[i - 1] !== id) && TEST_BODY_OPEN.test(stripLineComment2(lines[i]).trim()))
|
|
6283
|
+
testBodies2.add(id);
|
|
6284
|
+
}
|
|
6285
|
+
const blockOf = (i) => blockIds[withLines[i].startLine - 1];
|
|
6286
|
+
const passwordAt = stmts.findIndex((stmt, i) => testBodies2.has(blockOf(i)) && ENV_PASSWORD_ENTRY.test(stmt));
|
|
6287
|
+
if (passwordAt < 0)
|
|
6288
|
+
return void 0;
|
|
6289
|
+
const body = blockOf(passwordAt);
|
|
6290
|
+
let clickAt = passwordAt + 1;
|
|
6291
|
+
while (clickAt < stmts.length && FORM_INPUT.test(stmts[clickAt]) && !ANY_CLICK.test(stmts[clickAt]))
|
|
6292
|
+
clickAt++;
|
|
6293
|
+
if (clickAt >= stmts.length || blockOf(clickAt) !== body || !ANY_CLICK.test(stmts[clickAt]) || NAV_LOCATOR.test(stmts[clickAt]))
|
|
6294
|
+
return void 0;
|
|
6295
|
+
let end = clickAt + 1;
|
|
6296
|
+
for (; end < stmts.length && blockOf(end) === body; end++) {
|
|
6297
|
+
if (CLOSERS_ONLY.test(stmts[end]))
|
|
6298
|
+
continue;
|
|
6299
|
+
if (!ASSERT_OR_WAIT.test(stmts[end]) || ACTS.test(stmts[end]))
|
|
6300
|
+
return void 0;
|
|
6301
|
+
}
|
|
6302
|
+
return { after: withLines[clickAt].startLine, until: end < stmts.length ? withLines[end].startLine : Number.POSITIVE_INFINITY };
|
|
6303
|
+
}
|
|
6304
|
+
function isAccountControlCheck(stmt) {
|
|
6305
|
+
if (!VISIBILITY_MATCHER.test(stmt) || NEGATED_ASSERT.test(stmt) || OPTION_ABSENCE.test(stmt))
|
|
6306
|
+
return false;
|
|
6307
|
+
const role = ACCOUNT_CONTROL_ROLE.exec(stmt);
|
|
6308
|
+
if (!role)
|
|
6309
|
+
return false;
|
|
6310
|
+
const rest = stmt.slice(role.index + role[0].length, role.index + role[0].length + 300);
|
|
6311
|
+
const close = rest.indexOf("}");
|
|
6312
|
+
const options = close < 0 ? rest : rest.slice(0, close);
|
|
6313
|
+
const name = options.match(/(?:^|[\s,])name\s*:\s*/);
|
|
6314
|
+
return name !== null && ACCOUNT_EMAIL_ENV.test(options.slice(name.index + name[0].length));
|
|
6315
|
+
}
|
|
6316
|
+
function classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstMutLine, persistence, signedIn) {
|
|
5938
6317
|
if (mutated && hasAttributeTransition(stmts))
|
|
5939
6318
|
return { kind: "durable" };
|
|
5940
6319
|
const notices = [];
|
|
@@ -5944,6 +6323,13 @@ function classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstM
|
|
|
5944
6323
|
};
|
|
5945
6324
|
let presenceText;
|
|
5946
6325
|
const presence = () => presenceText ??= stmts.filter((o) => !ABSENCE_MATCHER.test(o.stmt) && !NEGATED_ASSERT.test(o.stmt)).map((o) => o.stmt).join("\n");
|
|
6326
|
+
let shownAccounts;
|
|
6327
|
+
const shown = () => {
|
|
6328
|
+
if (shownAccounts)
|
|
6329
|
+
return shownAccounts;
|
|
6330
|
+
const ref = new RegExp(entered.account.source, "g");
|
|
6331
|
+
return shownAccounts = new Set(stmts.filter((o) => !claimsAbsence(o.stmt)).flatMap((o) => Array.from(o.stmt.matchAll(ref), (m) => m[0])));
|
|
6332
|
+
};
|
|
5947
6333
|
for (const { stmt, startLine } of stmts) {
|
|
5948
6334
|
if (firstMutLine !== void 0 && startLine !== void 0 && startLine < firstMutLine)
|
|
5949
6335
|
continue;
|
|
@@ -5951,6 +6337,11 @@ function classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstM
|
|
|
5951
6337
|
continue;
|
|
5952
6338
|
if (COUNT_ONE.test(stmt) && quotedAssertedStrings(stmt).some((t) => clicked.has(t.toLowerCase())))
|
|
5953
6339
|
continue;
|
|
6340
|
+
if (isVacuousAccountAbsence(stmt, shown, entered.account))
|
|
6341
|
+
continue;
|
|
6342
|
+
if (signedIn && startLine !== void 0 && startLine > signedIn.after && startLine < signedIn.until && isAccountControlCheck(stmt)) {
|
|
6343
|
+
return { kind: "durable" };
|
|
6344
|
+
}
|
|
5954
6345
|
if (creditsOutcomeAlways(stmt, entered, startLine))
|
|
5955
6346
|
return { kind: "durable" };
|
|
5956
6347
|
if (mutated && !NEGATED_ASSERT.test(stmt) && MESSAGE_SURFACE.test(stmt) && OUTCOME_TEXT.test(assertedText(stmt))) {
|
|
@@ -6032,6 +6423,53 @@ function noticeOnlyExemption(clicked, noticeText, interactionStmts) {
|
|
|
6032
6423
|
function detectOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstMutLine) {
|
|
6033
6424
|
return classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstMutLine).kind !== "none";
|
|
6034
6425
|
}
|
|
6426
|
+
function clickLabel(stmt) {
|
|
6427
|
+
let label = null;
|
|
6428
|
+
for (const m of stmt.matchAll(CONTROL_LABEL))
|
|
6429
|
+
label = m[2] ?? m[3] ?? null;
|
|
6430
|
+
return label;
|
|
6431
|
+
}
|
|
6432
|
+
function teardownLines(lines) {
|
|
6433
|
+
const out = new Array(lines.length).fill(false);
|
|
6434
|
+
let open = false;
|
|
6435
|
+
let depth = 0;
|
|
6436
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6437
|
+
const ln = stripLineComment2(lines[i]).trim();
|
|
6438
|
+
let from = 0;
|
|
6439
|
+
if (!open) {
|
|
6440
|
+
const m = TEARDOWN_OPEN.exec(ln);
|
|
6441
|
+
if (!m)
|
|
6442
|
+
continue;
|
|
6443
|
+
open = true;
|
|
6444
|
+
depth = 0;
|
|
6445
|
+
from = m.index;
|
|
6446
|
+
}
|
|
6447
|
+
out[i] = true;
|
|
6448
|
+
let closed = false;
|
|
6449
|
+
let quote = null;
|
|
6450
|
+
for (let k = from; k < ln.length; k++) {
|
|
6451
|
+
const ch = ln[k];
|
|
6452
|
+
if (quote !== null) {
|
|
6453
|
+
if (ch === "\\")
|
|
6454
|
+
k++;
|
|
6455
|
+
else if (ch === quote)
|
|
6456
|
+
quote = null;
|
|
6457
|
+
continue;
|
|
6458
|
+
}
|
|
6459
|
+
if (ch === "'" || ch === '"' || ch === "`")
|
|
6460
|
+
quote = ch;
|
|
6461
|
+
else if (ch === "{")
|
|
6462
|
+
depth++;
|
|
6463
|
+
else if (ch === "}") {
|
|
6464
|
+
depth--;
|
|
6465
|
+
closed = true;
|
|
6466
|
+
}
|
|
6467
|
+
}
|
|
6468
|
+
if (depth <= 0 && closed)
|
|
6469
|
+
open = false;
|
|
6470
|
+
}
|
|
6471
|
+
return out;
|
|
6472
|
+
}
|
|
6035
6473
|
function validateAssertionStrength(code, opts = {}) {
|
|
6036
6474
|
const body = stripLoginScaffolding(code);
|
|
6037
6475
|
const lines = body.split("\n");
|
|
@@ -6075,7 +6513,13 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
6075
6513
|
const interactionStmts = inlineLocatorBindings(interactionStmtsRaw);
|
|
6076
6514
|
const blockIds = testBlockIds(lines);
|
|
6077
6515
|
const blockOfLine = (line) => blockIds?.[line - 1] ?? 0;
|
|
6078
|
-
const
|
|
6516
|
+
const hoistedReads = assertionStrengthHoistedReadsOn();
|
|
6517
|
+
const boundAt = hoistedReads ? /* @__PURE__ */ new Map() : void 0;
|
|
6518
|
+
const resolved = resolveSubjectBindings(stmts, scopedLocatorBindings(interactionStmtsWithLines, blockOfLine), blockOfLine, boundAt);
|
|
6519
|
+
const checks = boundAt === void 0 ? resolved : resolved.flatMap((s) => {
|
|
6520
|
+
const from = boundAt.get(s);
|
|
6521
|
+
return plainAssertion(s, from !== void 0 && actsBetween(interactionStmtsWithLines, from, s.startLine));
|
|
6522
|
+
});
|
|
6079
6523
|
const insideTest = linesInsideTestBlocks(lines);
|
|
6080
6524
|
const inTestOrder = (i) => insideTest === null || insideTest[interactionStmtsWithLines[i].startLine - 1] === true;
|
|
6081
6525
|
let mutated = opts.interactionPerformed === true;
|
|
@@ -6085,9 +6529,34 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
6085
6529
|
let navClicks = 0;
|
|
6086
6530
|
const persistence = { mutLines: [], reloadLines: [], reloadCalls: [] };
|
|
6087
6531
|
const openedPages = /* @__PURE__ */ new Set();
|
|
6532
|
+
let checkedPage = null;
|
|
6533
|
+
const savedOnCheckedPage = /* @__PURE__ */ new Set();
|
|
6534
|
+
const saveAt = (i, at) => {
|
|
6535
|
+
lastMutIdx = i;
|
|
6536
|
+
if (!inTestOrder(i))
|
|
6537
|
+
return;
|
|
6538
|
+
persistence.mutLines.push(at);
|
|
6539
|
+
if (firstMutIdx < 0)
|
|
6540
|
+
firstMutIdx = i;
|
|
6541
|
+
if (checkedPage !== null)
|
|
6542
|
+
savedOnCheckedPage.add(checkedPage);
|
|
6543
|
+
};
|
|
6544
|
+
const lastActionProof = opts.lastActionProof ?? "off";
|
|
6545
|
+
const teardown = lastActionProof === "off" ? null : teardownLines(lines);
|
|
6546
|
+
const acts = { last: null, prev: null, count: 0 };
|
|
6547
|
+
const noteAct = (i, at, kind) => {
|
|
6548
|
+
if (teardown === null || teardown[at - 1] || !inTestOrder(i))
|
|
6549
|
+
return;
|
|
6550
|
+
if (kind !== "enter")
|
|
6551
|
+
acts.count++;
|
|
6552
|
+
acts.prev = acts.last;
|
|
6553
|
+
acts.last = { i, kind };
|
|
6554
|
+
};
|
|
6088
6555
|
for (let i = 0; i < interactionStmts.length; i++) {
|
|
6089
6556
|
const ln = interactionStmts[i];
|
|
6090
6557
|
const at = interactionStmtsWithLines[i].startLine;
|
|
6558
|
+
if (teardown !== null && acts.last?.kind === "fill" && ENTER_PRESS.test(ln))
|
|
6559
|
+
noteAct(i, at, "enter");
|
|
6091
6560
|
if (RELOAD.test(ln)) {
|
|
6092
6561
|
persistence.reloadLines.push(at);
|
|
6093
6562
|
persistence.reloadCalls.push(at);
|
|
@@ -6095,32 +6564,31 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
6095
6564
|
const opened = ln.match(/\.goto\s*\(\s*([^,)]+)/);
|
|
6096
6565
|
if (opened) {
|
|
6097
6566
|
const page = opened[1].trim();
|
|
6098
|
-
|
|
6567
|
+
const path = namedPagePath(page);
|
|
6568
|
+
if (openedPages.has(page) || path !== null && savedOnCheckedPage.has(path))
|
|
6099
6569
|
persistence.reloadLines.push(at);
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
}
|
|
6570
|
+
openedPages.add(page);
|
|
6571
|
+
checkedPage = null;
|
|
6572
|
+
}
|
|
6573
|
+
if (/\bpage\s*\.\s*go(?:Back|Forward)\s*\(/.test(ln))
|
|
6574
|
+
checkedPage = null;
|
|
6575
|
+
const checked = URL_CHECK_ARG.exec(ln);
|
|
6576
|
+
if (checked)
|
|
6577
|
+
checkedPage = namedPagePath(checked[1] ?? checked[2]);
|
|
6103
6578
|
if (FORM_INPUT.test(ln)) {
|
|
6104
6579
|
mutated = true;
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
persistence.mutLines.push(at);
|
|
6108
|
-
if (firstMutIdx < 0)
|
|
6109
|
-
firstMutIdx = i;
|
|
6110
|
-
}
|
|
6580
|
+
saveAt(i, at);
|
|
6581
|
+
noteAct(i, at, "fill");
|
|
6111
6582
|
continue;
|
|
6112
6583
|
}
|
|
6113
6584
|
if (ANY_CLICK.test(ln)) {
|
|
6114
6585
|
if (NAV_LOCATOR.test(ln)) {
|
|
6115
6586
|
navClicks++;
|
|
6587
|
+
checkedPage = null;
|
|
6116
6588
|
} else {
|
|
6117
6589
|
mutated = true;
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
persistence.mutLines.push(at);
|
|
6121
|
-
if (firstMutIdx < 0)
|
|
6122
|
-
firstMutIdx = i;
|
|
6123
|
-
}
|
|
6590
|
+
saveAt(i, at);
|
|
6591
|
+
noteAct(i, at, "click");
|
|
6124
6592
|
}
|
|
6125
6593
|
}
|
|
6126
6594
|
}
|
|
@@ -6153,9 +6621,28 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
6153
6621
|
};
|
|
6154
6622
|
}
|
|
6155
6623
|
const orderedPersistence = firstMutLine !== void 0 ? persistence : void 0;
|
|
6156
|
-
const
|
|
6624
|
+
const signedIn = signInProofWindow(interactionStmts, interactionStmtsWithLines, lines, blockIds);
|
|
6625
|
+
const proof = classifyOutcomeProof(checks, mutated, reloaded, clickedControlNames(interactionStmts), entered, firstMutLine, orderedPersistence, signedIn);
|
|
6157
6626
|
if (proof.kind === "durable") {
|
|
6158
6627
|
const signals2 = base2({ interacted, mutated, navOnly, hasOutcomeProof: true, terminal });
|
|
6628
|
+
const { last, prev } = acts;
|
|
6629
|
+
if (teardown !== null && last !== null && acts.count >= 2 && last.kind !== "fill" && (last.kind === "enter" || isCommitClick(interactionStmts[last.i]) && !(prev?.kind === "click" && isDestructiveClick(interactionStmts[prev.i])))) {
|
|
6630
|
+
const lastLine = interactionStmtsWithLines[last.i].startLine;
|
|
6631
|
+
const clicked = clickedControlNames(interactionStmts);
|
|
6632
|
+
const after = classifyOutcomeProof(checks, mutated, reloaded, clicked, entered, lastLine, orderedPersistence, signedIn);
|
|
6633
|
+
const unproven = after.kind === "none" || after.kind === "notice" && noticeOnlyExemption(clicked, after.noticeText, interactionStmts) === null;
|
|
6634
|
+
if (unproven && lastActionProof === "on") {
|
|
6635
|
+
const line = (keptScaffoldingLines(code.split("\n"))[lastLine - 1] ?? lastLine - 1) + 1;
|
|
6636
|
+
return {
|
|
6637
|
+
ok: false,
|
|
6638
|
+
severity: "block",
|
|
6639
|
+
reason: `the last save (line ${line}) has no outcome check after it (the earlier check proves only the earlier action)`,
|
|
6640
|
+
signals: { ...signals2, lastActionUnproven: true }
|
|
6641
|
+
};
|
|
6642
|
+
}
|
|
6643
|
+
if (unproven)
|
|
6644
|
+
signals2.lastActionUnproven = true;
|
|
6645
|
+
}
|
|
6159
6646
|
return { ok: true, severity: "none", reason: "proves an outcome (count / diff / value / revealed content)", signals: signals2 };
|
|
6160
6647
|
}
|
|
6161
6648
|
if (mutated && proof.kind === "notice") {
|
|
@@ -6204,7 +6691,7 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
6204
6691
|
signals
|
|
6205
6692
|
};
|
|
6206
6693
|
}
|
|
6207
|
-
const urlProofAfter = interactionStmts.some((s, i) => i > lastMutIdx &&
|
|
6694
|
+
const urlProofAfter = interactionStmts.some((s, i) => i > lastMutIdx && isUrlProof(s));
|
|
6208
6695
|
if (urlProofAfter) {
|
|
6209
6696
|
return {
|
|
6210
6697
|
ok: false,
|
|
@@ -6241,7 +6728,7 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
6241
6728
|
signals
|
|
6242
6729
|
};
|
|
6243
6730
|
}
|
|
6244
|
-
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;
|
|
6731
|
+
var ERROR_BANNER, OUTCOME_ALWAYS, FORM_INPUT, ANY_CLICK, NAV_LOCATOR, URL_PROOF, POLL_OPEN, POLL_PAGE_URL, POLL_PAGE_URL_ONLY, POLL_CALLS_MAX, 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, SCOPING_STEP, 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, ACCOUNT_KEYS, ACCOUNT_ENV, ACCOUNT_CONST, 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, URL_CHECK_ARG, PLAIN_PAGE_PATH, FORM_CONTROL_LOCATOR, CONTROL_VALUE_MATCHER, ACCOUNT_EMAIL_ENV, ENV_PASSWORD_ENTRY, ACCOUNT_CONTROL_ROLE, ASSERT_OR_WAIT, ACTS, actLinesOf, CLOSERS_ONLY, TEST_BODY_OPEN, COPY_CONTROL, COPIED_NOTICE, SEND_CONTROL, SEND_NOTICE, MARK_READ_CONTROL, APP_DATA_FIELD, COMMIT_CONTROL, NOT_COMMIT_CONTROL, DESTRUCTIVE_CONTROL, ENTER_PRESS, CONTROL_LABEL, isCommitClick, isDestructiveClick, TEARDOWN_OPEN;
|
|
6245
6732
|
var init_assertion_strength = __esm({
|
|
6246
6733
|
"../shared/dist/assertion-strength.js"() {
|
|
6247
6734
|
"use strict";
|
|
@@ -6250,6 +6737,7 @@ var init_assertion_strength = __esm({
|
|
|
6250
6737
|
init_assertion_proof();
|
|
6251
6738
|
init_auth_handoff_coverage();
|
|
6252
6739
|
init_expect_call();
|
|
6740
|
+
init_auth_plan();
|
|
6253
6741
|
ERROR_BANNER = /(client-side exception|application error|something (?:went wrong|broke)|unhandled (?:exception|error|rejection)|runtime error|error boundary|an unexpected error|internal server error|\b5\d\d\s+(?:internal\s+)?server error|stack trace|chunkloaderror|hydration failed|maximum update depth exceeded)/i;
|
|
6254
6742
|
OUTCOME_ALWAYS = [
|
|
6255
6743
|
/\.toHaveCount\s*\(/,
|
|
@@ -6266,7 +6754,7 @@ var init_assertion_strength = __esm({
|
|
|
6266
6754
|
// form field value
|
|
6267
6755
|
/\.toMatch\s*\(/,
|
|
6268
6756
|
// string/regex content match (e.g. textContent() vs /[2-9] selected/)
|
|
6269
|
-
|
|
6757
|
+
/\.(?<!\.not\s*\.)\s*toBeDisabled\s*\(/,
|
|
6270
6758
|
// a control became DISABLED — a gating/validation outcome (not.toBeDisabled is the toBeEnabled twin)
|
|
6271
6759
|
/\.not\.toBeVisible\s*\(/,
|
|
6272
6760
|
/\.toBeHidden\s*\(/
|
|
@@ -6275,12 +6763,16 @@ var init_assertion_strength = __esm({
|
|
|
6275
6763
|
FORM_INPUT = /\.(fill|selectOption|setInputFiles|check|uncheck|setChecked|pressSequentially)\s*\(|keyboard\.type\s*\(/;
|
|
6276
6764
|
ANY_CLICK = /\.click\s*\(/;
|
|
6277
6765
|
NAV_LOCATOR = /getByRole\s*\(\s*['"`](?:link|menuitem)['"`]/i;
|
|
6278
|
-
URL_PROOF = /\.toHaveURL\s*\(|\.waitForURL\s*\(|\bexpect\s*\(\s*(?:await\s+)?page\s*\.\s*url\s*\(\s*\)\s*\)
|
|
6766
|
+
URL_PROOF = /\.toHaveURL\s*\(|\.waitForURL\s*\(|\bexpect\s*\(\s*(?:await\s+)?page\s*\.\s*url\s*\(\s*\)\s*\)\s*\.\s*(?:toBe|toEqual|toStrictEqual|toMatch|toContain)\s*\(/;
|
|
6767
|
+
POLL_OPEN = /\bexpect\s*\.\s*poll\s*\(/g;
|
|
6768
|
+
POLL_PAGE_URL = /^page\s*\.\s*url\s*\(\s*\)/;
|
|
6769
|
+
POLL_PAGE_URL_ONLY = /^page\s*\.\s*url\s*\(\s*\)$/;
|
|
6770
|
+
POLL_CALLS_MAX = 8;
|
|
6279
6771
|
RELOAD = /\.reload\s*\(/;
|
|
6280
6772
|
EXPECT_STMT = /\bexpect\s*[.(]/;
|
|
6281
6773
|
BLOCK_SCAFFOLD2 = /=>|\b(?:test|describe|it|beforeEach|afterEach|beforeAll|afterAll|function)\b/;
|
|
6282
6774
|
TEST_BLOCK_OPEN = /^(?:test|it)(?:\.(?:only|skip|fixme|slow|serial|describe\.serial))?\s*\(|^(?:test\.)?(?:beforeEach|beforeAll)\s*\(/;
|
|
6283
|
-
LOCATOR_BINDING = /^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s
|
|
6775
|
+
LOCATOR_BINDING = /^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=(?:\s*(?!\s)|\s*(?=\s(?:getByRole|getByText|getByLabel|getByPlaceholder|getByTestId|getByAltText|getByTitle|locator)\s*\())(.+?(?:getByRole|getByText|getByLabel|getByPlaceholder|getByTestId|getByAltText|getByTitle|locator)\s*\(.+)$/;
|
|
6284
6776
|
EXPECT_OPEN = /\bexpect(?:\.soft)?\s*\(/;
|
|
6285
6777
|
STATE_MATCHER = /\.(toBeChecked|toBeVisible|toHaveValue|toBeDisabled|toBeEnabled)\s*\(/;
|
|
6286
6778
|
CHROME_LOCATOR = /getByRole\s*\(\s*['"`](?:heading|navigation|banner|main|contentinfo|complementary|region|search|tablist|tab|button|link|menuitem|textbox|searchbox|combobox|spinbutton)['"`]/i;
|
|
@@ -6300,7 +6792,7 @@ var init_assertion_strength = __esm({
|
|
|
6300
6792
|
EMPTY_STATE_TEXT = /\bno\b[\s\S]{0,24}?\b(yet|found|results?|items?|favou?rites?|entries|records?|matches|posts?|comments?)\b|nothing (?:here|yet|to show|found)|\bempty\b|\(\s*0\s*\)|\b0\s+(?:favou?rites?|results?|items?|entries|records?|matches|posts?|comments?)\b/i;
|
|
6301
6793
|
AUTH_START = /^\s*\/\/\s*>>>\s*VALIDATE_QA_AUTH/;
|
|
6302
6794
|
AUTH_END = /^\s*\/\/\s*<<<\s*VALIDATE_QA_AUTH/;
|
|
6303
|
-
LOGIN_CALL_LINE = /^\s*await\s+__vqaLogin\s*\(\s*page\s*\)\s
|
|
6795
|
+
LOGIN_CALL_LINE = /^\s*await\s+__vqaLogin\s*\(\s*page\s*\)\s*(?:;\s*)?$/;
|
|
6304
6796
|
INBOX_START = /^\s*\/\/\s*>>>\s*VALIDATE_QA_INBOX/;
|
|
6305
6797
|
INBOX_END = /^\s*\/\/\s*<<<\s*VALIDATE_QA_INBOX/;
|
|
6306
6798
|
NEGATED_ASSERT = /\.not\s*\.\s*to[A-Z]\w*|\.toBeHidden\s*\(/;
|
|
@@ -6310,7 +6802,7 @@ var init_assertion_strength = __esm({
|
|
|
6310
6802
|
TITLE_LINE = /^\s*(?:test|describe)(?:\.\w+)?\s*\(/;
|
|
6311
6803
|
FAILURE_STATUS_ASSERT = [
|
|
6312
6804
|
/\.status\s*\(\s*\)[^;]{0,200}\.(?:toBeGreaterThanOrEqual|toBeGreaterThan)\s*\(\s*(?:399|[45]\d\d)\b/,
|
|
6313
|
-
/\bexpect\s*\(\s*[^;]{0,200}\bstatus\s*(?:\(\s*\))
|
|
6805
|
+
/\bexpect\s*\(\s*[^;]{0,200}\bstatus\s*(?:\(\s*\)\s*)?>=?\s*(?:399|[45]\d\d)\b[^;]{0,200}\)\s*\.\s*(?:toBe\s*\(\s*true|toBeTruthy)/,
|
|
6314
6806
|
/\bexpect\s*\([^;]{0,200}\.ok\s*\(\s*\)\s*\)\s*\.\s*(?:toBe\s*\(\s*false\s*\)|toBeFalsy\s*\()/
|
|
6315
6807
|
];
|
|
6316
6808
|
FAILED_LIST_ASSERT = /\bexpect\s*\(\s*[^;]{0,200}(?:fail|abort|error)\w{0,40}(?:\.length)?\s*\)\s*\.\s*(?:toBeGreaterThan\s*\(\s*0|toHaveLength\s*\(\s*[1-9]|not\s*\.\s*toHaveLength\s*\(\s*0|not\s*\.\s*toEqual\s*\(\s*\[\s*\])/i;
|
|
@@ -6325,6 +6817,7 @@ var init_assertion_strength = __esm({
|
|
|
6325
6817
|
NOT_BEFORE_MATCHER = /\.not\s*\.\s*(?=to[A-Z])/g;
|
|
6326
6818
|
ERROR_TEXT_WORDS = /error|fail|wrong|try again|unable|could ?n.?t|can.?t|problem|invalid|unavailable/i;
|
|
6327
6819
|
TEXT_REGEX_ARG = /(?:getByText|hasText|getByRole\([^)]{0,200}name)\s*[:(]\s*(\/(?:\\.|[^/\\\n])+\/[a-z]*)/;
|
|
6820
|
+
SCOPING_STEP = /(?:\s*\.\s*(?:first|last|nth)\s*\([^()]*\))*\s*\.\s*(?:getBy[A-Z]\w*|locator|filter)\s*\(/y;
|
|
6328
6821
|
NEGATED_TEXT = /\.not\s*\.\s*(?:toHaveText|toContainText)\s*\(\s*(?:['"`]([^'"`]*)['"`]|\/((?:\\.|[^/\\\n])+)\/)?/;
|
|
6329
6822
|
ASYNC_DESTRUCTURE_OPEN = /async\s*\(\s*\{/g;
|
|
6330
6823
|
REQUEST_WORD = /\brequest\b/;
|
|
@@ -6351,6 +6844,9 @@ var init_assertion_strength = __esm({
|
|
|
6351
6844
|
ENV_READ = String.raw`process\.env(?:(?:\?\.|\.)([A-Za-z_]\w*)|(?:\?\.)?\[\s*['"]([A-Za-z_]\w*)['"]\s*\])`;
|
|
6352
6845
|
ENTERED_ENV = new RegExp(String.raw`\.(?:fill|pressSequentially)\s*\(\s*${ENV_READ}|keyboard\.type\s*\(\s*${ENV_READ}`, "g");
|
|
6353
6846
|
HAVE_VALUE_ENV = new RegExp(String.raw`\.toHaveValue\s*\(\s*${ENV_READ}`);
|
|
6847
|
+
ACCOUNT_KEYS = TEST_USER_ENV_KEYS.join("|");
|
|
6848
|
+
ACCOUNT_ENV = `process\\.env(?:\\??\\.(?:${ACCOUNT_KEYS})\\b|(?:\\?\\.)?\\[\\s*['"\`](?:${ACCOUNT_KEYS})['"\`]\\s*\\])`;
|
|
6849
|
+
ACCOUNT_CONST = new RegExp(`\\b(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)\\s*(?::\\s*string\\s*)?=\\s*${ACCOUNT_ENV}`, "g");
|
|
6354
6850
|
IN_FLIGHT_TEXT = /^(?:(?:sending|saving|loading|searching|checking|processing|submitting|preparing|updating|please wait|working|one moment)\b[^.]{0,40}\.{2,3}|\.{3}|…)$/i;
|
|
6355
6851
|
LOCATOR_VALUE_READ = /\.(?:count|textContent|innerText|innerHTML|inputValue|getAttribute|isVisible|isHidden|isChecked|isEnabled|isDisabled|isEditable|allTextContents|allInnerTexts|evaluate|evaluateAll|boundingBox|all)\s*\(/;
|
|
6356
6852
|
LOCATOR_EXPR = /^(?:this\.)?page\s*\.\s*(?:getBy\w+|locator)\s*\(/;
|
|
@@ -6362,20 +6858,47 @@ var init_assertion_strength = __esm({
|
|
|
6362
6858
|
IS_VISIBLE_TRUE = /^\)*\s*\.\s*(?:toBe\s*\(\s*true\s*\)|toBeTruthy\s*\(\s*\))/;
|
|
6363
6859
|
PAGE_VALUE_MATCHER = /^\)\s*\.\s*(?:toBe|toEqual|toStrictEqual|toMatch|toContain)\s*\(/;
|
|
6364
6860
|
COUNT_ONE = /\.\s*toHaveCount\s*\(\s*1\s*\)/;
|
|
6861
|
+
URL_CHECK_ARG = /\bpage\s*\.\s*waitForURL\s*\(\s*([^,)]+)|\bexpect(?:\s*\.\s*soft)?\s*\(\s*page\s*\)\s*\.\s*toHaveURL\s*\(\s*([^,)]+)/;
|
|
6862
|
+
PLAIN_PAGE_PATH = /^\/[\w\-./~%]*(?:[?#][^\s]*)?$/;
|
|
6365
6863
|
FORM_CONTROL_LOCATOR = /getByRole\s*\(\s*['"`](?:combobox|switch|checkbox|radio|textbox|searchbox|spinbutton|slider)['"`]|getByLabel\s*\(|getByPlaceholder\s*\(/;
|
|
6366
6864
|
CONTROL_VALUE_MATCHER = /\.(?:toHaveValue|toHaveText|toContainText)\s*\(|\.toHaveAttribute\s*\(\s*['"`]aria-checked['"`]/;
|
|
6865
|
+
ACCOUNT_EMAIL_ENV = /^process\.env(?:(?:\?\.|\.)TEST_USER_EMAIL\b|(?:\?\.)?\[\s*['"]TEST_USER_EMAIL['"]\s*\])/;
|
|
6866
|
+
ENV_PASSWORD_ENTRY = /\.(?:fill|pressSequentially)\s*\(\s*process\.env(?:(?:\?\.|\.)TEST_USER_PASSWORD\b|(?:\?\.)?\[\s*['"]TEST_USER_PASSWORD['"]\s*\])/;
|
|
6867
|
+
ACCOUNT_CONTROL_ROLE = /getByRole\s*\(\s*['"`](?:button|link|menuitem)['"`]\s*,\s*\{/;
|
|
6868
|
+
ASSERT_OR_WAIT = /^(?:await\s+)?(?:expect(?:\.soft|\.poll)?\s*\(|[\w$]+(?:\.[\w$]+)*\.waitFor[A-Za-z]*\s*\()/;
|
|
6869
|
+
ACTS = /\.(?:click|dblclick|tap|press|pressSequentially|fill|clear|type|check|uncheck|setChecked|selectOption|setInputFiles|dispatchEvent|hover|focus|dragTo|goto|reload|goBack|goForward|evaluate|evaluateHandle)\s*\(|\b(?:keyboard|mouse|touchscreen)\s*\.|\.toPass\s*\(/;
|
|
6870
|
+
actLinesOf = /* @__PURE__ */ new WeakMap();
|
|
6871
|
+
CLOSERS_ONLY = /^[})\];,\s]+$/;
|
|
6872
|
+
TEST_BODY_OPEN = /^(?:test|it)(?:\.(?:only|skip|fixme|slow))?\s*\(/;
|
|
6367
6873
|
COPY_CONTROL = /\bcopy\b/i;
|
|
6368
6874
|
COPIED_NOTICE = /\bcopied\b/i;
|
|
6369
6875
|
SEND_CONTROL = /\b(send|submit|subscribe|sign up for|contact|feedback|join (the )?waitlist)\b/i;
|
|
6370
6876
|
SEND_NOTICE = /\b(thank|thanks|sent|subscribed|received|in touch)\b/i;
|
|
6371
6877
|
MARK_READ_CONTROL = /\bmark(?:ed)? as read\b|\bmark all(?: as)? read\b/i;
|
|
6372
6878
|
APP_DATA_FIELD = /\b(title|name of|amount|price|quantity|sku|status|due|assignee|project|ticket|invoice|order)\b/i;
|
|
6879
|
+
COMMIT_CONTROL = /\b(?:save|submit|update|apply|add|create|post|send|publish)\b|\bconfirm\s+changes\b/i;
|
|
6880
|
+
NOT_COMMIT_CONTROL = /\b(?:delete|remove|cancel|close|dismiss|discard|back|sign\s*out|log\s*out|logout|done|ok|got\s+it|export|download)\b/i;
|
|
6881
|
+
DESTRUCTIVE_CONTROL = /\b(?:delete|remove|discard)\b/i;
|
|
6882
|
+
ENTER_PRESS = /\.press\s*\(\s*['"`]Enter['"`]/;
|
|
6883
|
+
CONTROL_LABEL = /(?:name\s*:\s*|getBy(?:Text|Label|Title|TestId)\s*\(\s*|has-text\(\s*)(?:(['"`])((?:(?!\1).)*)\1|\/((?:\\.|[^/\\])+)\/)/g;
|
|
6884
|
+
isCommitClick = (stmt) => {
|
|
6885
|
+
const label = clickLabel(stmt);
|
|
6886
|
+
return label !== null && COMMIT_CONTROL.test(label) && !NOT_COMMIT_CONTROL.test(label) && !EXCLUDE_AFFORDANCE.test(label);
|
|
6887
|
+
};
|
|
6888
|
+
isDestructiveClick = (stmt) => {
|
|
6889
|
+
const label = clickLabel(stmt);
|
|
6890
|
+
return label !== null && DESTRUCTIVE_CONTROL.test(label);
|
|
6891
|
+
};
|
|
6892
|
+
TEARDOWN_OPEN = /\bfinally\s*\{|^(?:test\.)?(?:after|before)(?:Each|All)\s*\(/;
|
|
6373
6893
|
}
|
|
6374
6894
|
});
|
|
6375
6895
|
|
|
6376
6896
|
// ../shared/dist/run-classification.js
|
|
6377
|
-
function
|
|
6378
|
-
|
|
6897
|
+
function isCancelledReason(error2) {
|
|
6898
|
+
if (typeof error2 !== "string")
|
|
6899
|
+
return false;
|
|
6900
|
+
const text = error2.trim();
|
|
6901
|
+
return CANCELLED_REASON_PREFIXES.some((prefix) => text.startsWith(prefix));
|
|
6379
6902
|
}
|
|
6380
6903
|
function isHeldHealRun(run) {
|
|
6381
6904
|
return run?.status === "PASSED" && typeof run.error === "string" && run.error.startsWith(HEAL_PATCH_HELD_RUN_ERROR_PREFIX);
|
|
@@ -6408,7 +6931,7 @@ function pickLatestHealthRun(runs) {
|
|
|
6408
6931
|
for (const run of runs) {
|
|
6409
6932
|
if (run.mode === "auth-setup")
|
|
6410
6933
|
continue;
|
|
6411
|
-
if (run.status === "ERROR" &&
|
|
6934
|
+
if (run.status === "ERROR" && isCancelledReason(run.error))
|
|
6412
6935
|
continue;
|
|
6413
6936
|
if (isHeldHealRun(run))
|
|
6414
6937
|
continue;
|
|
@@ -6426,6 +6949,15 @@ function pickDisplayRun(runs) {
|
|
|
6426
6949
|
return latest;
|
|
6427
6950
|
return pickLatestHealthRun(runs) ?? latest;
|
|
6428
6951
|
}
|
|
6952
|
+
function featureRowRunStatus(runs) {
|
|
6953
|
+
const latest = runs.find((run) => run.mode !== "auth-setup");
|
|
6954
|
+
if (!latest)
|
|
6955
|
+
return null;
|
|
6956
|
+
const decisive = pickLatestHealthRun(runs.filter((run) => !isEnvironmentalError(run)));
|
|
6957
|
+
if (decisive)
|
|
6958
|
+
return decisive.status;
|
|
6959
|
+
return latest.status === "PENDING" || latest.status === "RUNNING" ? latest.status : null;
|
|
6960
|
+
}
|
|
6429
6961
|
function isEnvironmentalError(run) {
|
|
6430
6962
|
if (!run)
|
|
6431
6963
|
return false;
|
|
@@ -6457,7 +6989,7 @@ function classifyErrorMessage(error2) {
|
|
|
6457
6989
|
return "config_issue";
|
|
6458
6990
|
if (EMAIL_NOT_RECEIVED_PREFIXES.some((prefix) => normalized.startsWith(prefix)))
|
|
6459
6991
|
return "email_not_received";
|
|
6460
|
-
if (
|
|
6992
|
+
if (isCancelledReason(error2))
|
|
6461
6993
|
return "cancelled";
|
|
6462
6994
|
if (isRetriedRunInterruption(error2))
|
|
6463
6995
|
return "interrupted";
|
|
@@ -6492,7 +7024,7 @@ function heldHealRunWhere() {
|
|
|
6492
7024
|
function excludeCancelledRunsWhere() {
|
|
6493
7025
|
return {
|
|
6494
7026
|
NOT: [
|
|
6495
|
-
{ status: "ERROR", error: { not: null, startsWith:
|
|
7027
|
+
{ status: "ERROR", OR: CANCELLED_REASON_PREFIXES.map((prefix) => ({ error: { not: null, startsWith: prefix } })) },
|
|
6496
7028
|
heldHealRunWhere()
|
|
6497
7029
|
]
|
|
6498
7030
|
};
|
|
@@ -6570,11 +7102,25 @@ function reportedBugWhere() {
|
|
|
6570
7102
|
function bugReviewStatus(status) {
|
|
6571
7103
|
return status === "QUARANTINED" ? "confirmed" : "needs-review";
|
|
6572
7104
|
}
|
|
6573
|
-
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;
|
|
7105
|
+
var USER_CANCELLED_RUN_ERROR, PLATFORM_SUPPORT_CANCELLED_RUN_ERROR, PLATFORM_ADMIN_CANCELLED_RUN_ERROR, ADMIN_FORCE_CANCELLED_RUN_ERROR, CHAIN_CANCELLED_RUN_ERROR, RETRY_HELD_RUN_ERROR, CANCELLED_REASON_PREFIXES, isUserCancelledError, 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, FEATURE_ROW_RUN_WINDOW, BUG_SURFACE_SUPPRESSING_TAGS, QUARANTINE_CLOSE_TAGS, BUG_CANDIDATE_TAG, BUG_TEST_BROKEN_TAG;
|
|
6574
7106
|
var init_run_classification = __esm({
|
|
6575
7107
|
"../shared/dist/run-classification.js"() {
|
|
6576
7108
|
"use strict";
|
|
6577
7109
|
USER_CANCELLED_RUN_ERROR = "Cancelled by user";
|
|
7110
|
+
PLATFORM_SUPPORT_CANCELLED_RUN_ERROR = "Cancelled by platform support";
|
|
7111
|
+
PLATFORM_ADMIN_CANCELLED_RUN_ERROR = "Cancelled by platform admin";
|
|
7112
|
+
ADMIN_FORCE_CANCELLED_RUN_ERROR = "Admin force-cancelled all active runs";
|
|
7113
|
+
CHAIN_CANCELLED_RUN_ERROR = "Cancelled: parent run cancelled";
|
|
7114
|
+
RETRY_HELD_RUN_ERROR = "Cancelled: auto generation paused";
|
|
7115
|
+
CANCELLED_REASON_PREFIXES = [
|
|
7116
|
+
USER_CANCELLED_RUN_ERROR,
|
|
7117
|
+
PLATFORM_SUPPORT_CANCELLED_RUN_ERROR,
|
|
7118
|
+
PLATFORM_ADMIN_CANCELLED_RUN_ERROR,
|
|
7119
|
+
ADMIN_FORCE_CANCELLED_RUN_ERROR,
|
|
7120
|
+
CHAIN_CANCELLED_RUN_ERROR,
|
|
7121
|
+
RETRY_HELD_RUN_ERROR
|
|
7122
|
+
];
|
|
7123
|
+
isUserCancelledError = isCancelledReason;
|
|
6578
7124
|
HEAL_PATCH_HELD_RUN_ERROR_PREFIX = "Heal patch held";
|
|
6579
7125
|
RUNNER_INTERRUPT_ERROR_PREFIX = "Runner shutting down";
|
|
6580
7126
|
SERVER_INTERRUPT_ERROR_PREFIX = "Run interrupted by server restart";
|
|
@@ -6603,9 +7149,10 @@ var init_run_classification = __esm({
|
|
|
6603
7149
|
];
|
|
6604
7150
|
ENVIRONMENTAL_ERROR_STORED_PREFIXES = [
|
|
6605
7151
|
...ENVIRONMENTAL_ERROR_PREFIXES.flatMap((prefix) => [prefix, `Error: ${prefix}`]),
|
|
6606
|
-
// Written by the
|
|
6607
|
-
|
|
7152
|
+
// Written by the cancels as is, with no prefix wrapper.
|
|
7153
|
+
...CANCELLED_REASON_PREFIXES
|
|
6608
7154
|
];
|
|
7155
|
+
FEATURE_ROW_RUN_WINDOW = 10;
|
|
6609
7156
|
BUG_SURFACE_SUPPRESSING_TAGS = ["@rate-limit-blocked", "@quota-exceeded", "@auth-lost"];
|
|
6610
7157
|
QUARANTINE_CLOSE_TAGS = [
|
|
6611
7158
|
"@bug-found",
|
|
@@ -9010,205 +9557,31 @@ function sanitizeAdminViewReturnTo(value) {
|
|
|
9010
9557
|
return ADMIN_VIEW_DEFAULT_RETURN_TO;
|
|
9011
9558
|
if (!trimmed.startsWith("/") || trimmed.startsWith("//"))
|
|
9012
9559
|
return ADMIN_VIEW_DEFAULT_RETURN_TO;
|
|
9013
|
-
if (trimmed.includes("\\") || /[\s\u0000-\u001f\u007f]/.test(trimmed))
|
|
9014
|
-
return ADMIN_VIEW_DEFAULT_RETURN_TO;
|
|
9015
|
-
let url;
|
|
9016
|
-
try {
|
|
9017
|
-
url = new URL(trimmed, PLACEHOLDER_ORIGIN);
|
|
9018
|
-
} catch {
|
|
9019
|
-
return ADMIN_VIEW_DEFAULT_RETURN_TO;
|
|
9020
|
-
}
|
|
9021
|
-
if (url.origin !== PLACEHOLDER_ORIGIN)
|
|
9022
|
-
return ADMIN_VIEW_DEFAULT_RETURN_TO;
|
|
9023
|
-
if (BLOCKED_PATH_RE.test(url.pathname))
|
|
9024
|
-
return ADMIN_VIEW_DEFAULT_RETURN_TO;
|
|
9025
|
-
const out = `${url.pathname}${url.search}${url.hash}`;
|
|
9026
|
-
if (out.startsWith("//") || out.includes("\\"))
|
|
9027
|
-
return ADMIN_VIEW_DEFAULT_RETURN_TO;
|
|
9028
|
-
return out;
|
|
9029
|
-
}
|
|
9030
|
-
var ADMIN_VIEW_DEFAULT_RETURN_TO, MAX_RETURN_TO_LENGTH, PLACEHOLDER_ORIGIN, BLOCKED_PATH_RE;
|
|
9031
|
-
var init_admin_view_return_to = __esm({
|
|
9032
|
-
"../shared/dist/admin-view-return-to.js"() {
|
|
9033
|
-
"use strict";
|
|
9034
|
-
ADMIN_VIEW_DEFAULT_RETURN_TO = "/sessions";
|
|
9035
|
-
MAX_RETURN_TO_LENGTH = 2048;
|
|
9036
|
-
PLACEHOLDER_ORIGIN = "https://admin-view.invalid";
|
|
9037
|
-
BLOCKED_PATH_RE = /^\/(?:login|admin-view)/i;
|
|
9038
|
-
}
|
|
9039
|
-
});
|
|
9040
|
-
|
|
9041
|
-
// ../shared/dist/auth-plan.js
|
|
9042
|
-
function hasInlineLoginSteps(playwrightCode) {
|
|
9043
|
-
if (!playwrightCode)
|
|
9044
|
-
return false;
|
|
9045
|
-
return /process\.env\.TEST_USER_EMAIL!?/.test(playwrightCode) && /process\.env\.TEST_USER_PASSWORD!?/.test(playwrightCode);
|
|
9046
|
-
}
|
|
9047
|
-
function hasGeneratedAuthGuard(playwrightCode) {
|
|
9048
|
-
if (!playwrightCode)
|
|
9049
|
-
return false;
|
|
9050
|
-
return /\/\/ >>> VALIDATE_QA_AUTH v(\d+)\b[\s\S]*?process\.env\.AUTH_FILE[\s\S]*?\/\/ <<< VALIDATE_QA_AUTH v\1\b/.test(playwrightCode);
|
|
9051
|
-
}
|
|
9052
|
-
function legacyNeedsCookieState(tags, code, sharedAuthInjectEnabled) {
|
|
9053
|
-
if (!sharedAuthInjectEnabled)
|
|
9054
|
-
return false;
|
|
9055
|
-
if (tags.includes("@auth"))
|
|
9056
|
-
return false;
|
|
9057
|
-
if (tags.includes("@api"))
|
|
9058
|
-
return true;
|
|
9059
|
-
if (!tags.includes("@needs-auth"))
|
|
9060
|
-
return false;
|
|
9061
|
-
if (sharedAuthInjectEnabled && tags.includes("@auth-guarded") && hasGeneratedAuthGuard(code))
|
|
9062
|
-
return true;
|
|
9063
|
-
const isGeneratedVariant = tags.includes("@edge") || tags.includes("@bug");
|
|
9064
|
-
const hasLegacyInlineLogin = tags.includes("@discovered") && !isGeneratedVariant && hasInlineLoginSteps(code);
|
|
9065
|
-
return !tags.includes("@inline-login") && !hasLegacyInlineLogin;
|
|
9066
|
-
}
|
|
9067
|
-
function codeReadsStoredCredentials(code) {
|
|
9068
|
-
if (!code)
|
|
9069
|
-
return false;
|
|
9070
|
-
if (/__vqaLogin\b/.test(code))
|
|
9071
|
-
return true;
|
|
9072
|
-
for (const m of code.matchAll(/TEST_[A-Z0-9_]+/g)) {
|
|
9073
|
-
if (/_(?:EMAIL|PASSWORD)/.test(m[0]))
|
|
9074
|
-
return true;
|
|
9075
|
-
}
|
|
9076
|
-
for (const m of code.matchAll(/process\.env\s*\[([^\]]*)\]?/g)) {
|
|
9077
|
-
if (!/^\s*(['"])[^'"`]*\1\s*$/.test(m[1]))
|
|
9078
|
-
return true;
|
|
9079
|
-
}
|
|
9080
|
-
return false;
|
|
9081
|
-
}
|
|
9082
|
-
function explicitAuthTypeTags(t) {
|
|
9083
|
-
const found = /* @__PURE__ */ new Set();
|
|
9084
|
-
for (const tag of t) {
|
|
9085
|
-
if (AUTH_TYPE_TAGS.includes(tag))
|
|
9086
|
-
found.add(tag);
|
|
9087
|
-
else if (Object.hasOwn(AUTH_TAG_ALIASES, tag))
|
|
9088
|
-
found.add(AUTH_TAG_ALIASES[tag]);
|
|
9089
|
-
}
|
|
9090
|
-
return [...found];
|
|
9091
|
-
}
|
|
9092
|
-
function invalidPlan(configurationError) {
|
|
9093
|
-
return {
|
|
9094
|
-
version: 1,
|
|
9095
|
-
source: "invalid",
|
|
9096
|
-
acquisition: "unsupported",
|
|
9097
|
-
presentation: "none",
|
|
9098
|
-
needsCookieState: false,
|
|
9099
|
-
needsStandingHeaders: false,
|
|
9100
|
-
standingHeadersMode: "none",
|
|
9101
|
-
needsLoginCreds: false,
|
|
9102
|
-
requiredEnvKeys: [],
|
|
9103
|
-
configurationError
|
|
9104
|
-
};
|
|
9105
|
-
}
|
|
9106
|
-
function explicitPlan(authTypeTag2, acquisition, presentation, opts) {
|
|
9107
|
-
return {
|
|
9108
|
-
version: 1,
|
|
9109
|
-
source: "explicit",
|
|
9110
|
-
authTypeTag: authTypeTag2,
|
|
9111
|
-
acquisition,
|
|
9112
|
-
presentation,
|
|
9113
|
-
needsCookieState: opts.cookie,
|
|
9114
|
-
needsStandingHeaders: opts.standingMode === "required",
|
|
9115
|
-
standingHeadersMode: opts.standingMode,
|
|
9116
|
-
needsLoginCreds: TEST_USER_ENV_KEYS.every((k) => opts.requiredEnvKeys.includes(k)),
|
|
9117
|
-
requiredEnvKeys: opts.requiredEnvKeys
|
|
9118
|
-
};
|
|
9119
|
-
}
|
|
9120
|
-
function deriveAuthPlan(tags, code, opts) {
|
|
9121
|
-
const t = Array.isArray(tags) ? tags : [];
|
|
9122
|
-
const sharedFlag = opts?.sharedAuthInjectEnabled ?? false;
|
|
9123
|
-
const isApi = t.includes("@api");
|
|
9124
|
-
const explicit = isApi ? explicitAuthTypeTags(t) : [];
|
|
9125
|
-
if (explicit.length > 0) {
|
|
9126
|
-
if (t.includes("@auth")) {
|
|
9127
|
-
return invalidPlan("@auth (self-login UI) cannot be combined with an explicit @api auth-type tag");
|
|
9128
|
-
}
|
|
9129
|
-
const loginCompose = explicit.filter((e) => e === AUTH_TAG_COOKIE_STATE || e === AUTH_TAG_LOGIN_COOKIE || e === AUTH_TAG_LOGIN_BEARER);
|
|
9130
|
-
if (explicit.length === 2 && explicit.includes(AUTH_TAG_STANDING) && loginCompose.length === 1) {
|
|
9131
|
-
const composedKeys = [...TEST_USER_ENV_KEYS, API_AUTH_HEADERS_ENV_KEY];
|
|
9132
|
-
switch (loginCompose[0]) {
|
|
9133
|
-
case AUTH_TAG_COOKIE_STATE:
|
|
9134
|
-
return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
9135
|
-
case AUTH_TAG_LOGIN_COOKIE:
|
|
9136
|
-
return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
9137
|
-
case AUTH_TAG_LOGIN_BEARER:
|
|
9138
|
-
return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
9139
|
-
}
|
|
9140
|
-
}
|
|
9141
|
-
if (explicit.length > 1) {
|
|
9142
|
-
return invalidPlan(`conflicting auth-type tags: ${explicit.join(", ")}`);
|
|
9143
|
-
}
|
|
9144
|
-
switch (explicit[0]) {
|
|
9145
|
-
case AUTH_TAG_UNSUPPORTED:
|
|
9146
|
-
return {
|
|
9147
|
-
...invalidPlan("auth scheme not yet drivable (e.g. HMAC / mTLS / provider-replay) \u2014 needs manual configuration")
|
|
9148
|
-
};
|
|
9149
|
-
case AUTH_TAG_NONE:
|
|
9150
|
-
return explicitPlan(AUTH_TAG_NONE, "none", "none", { cookie: false, standingMode: "none", requiredEnvKeys: [] });
|
|
9151
|
-
case AUTH_TAG_STANDING:
|
|
9152
|
-
return explicitPlan(AUTH_TAG_STANDING, "standing-secret", "standing-header", { cookie: false, standingMode: "required", requiredEnvKeys: [API_AUTH_HEADERS_ENV_KEY] });
|
|
9153
|
-
case AUTH_TAG_LOGIN_BEARER:
|
|
9154
|
-
return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "none", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
9155
|
-
case AUTH_TAG_LOGIN_COOKIE:
|
|
9156
|
-
return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
9157
|
-
case AUTH_TAG_COOKIE_STATE:
|
|
9158
|
-
return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
9159
|
-
}
|
|
9560
|
+
if (trimmed.includes("\\") || /[\s\u0000-\u001f\u007f]/.test(trimmed))
|
|
9561
|
+
return ADMIN_VIEW_DEFAULT_RETURN_TO;
|
|
9562
|
+
let url;
|
|
9563
|
+
try {
|
|
9564
|
+
url = new URL(trimmed, PLACEHOLDER_ORIGIN);
|
|
9565
|
+
} catch {
|
|
9566
|
+
return ADMIN_VIEW_DEFAULT_RETURN_TO;
|
|
9160
9567
|
}
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9164
|
-
|
|
9165
|
-
const
|
|
9166
|
-
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
acquisition: cookie ? "browser-bootstrap" : authedLegacy ? "request-login" : "none",
|
|
9170
|
-
presentation: authedLegacy ? "cookie" : "none",
|
|
9171
|
-
needsCookieState: cookie,
|
|
9172
|
-
needsStandingHeaders: false,
|
|
9173
|
-
// A legacy bare-@api test keeps injecting standing headers IF the project configured them
|
|
9174
|
-
// (preserving today's behavior) — but does not require them. Non-@api legacy tests never inject.
|
|
9175
|
-
standingHeadersMode: isApi ? "legacy-if-configured" : "none",
|
|
9176
|
-
needsLoginCreds,
|
|
9177
|
-
requiredEnvKeys: needsLoginCreds ? [...TEST_USER_ENV_KEYS] : []
|
|
9178
|
-
};
|
|
9179
|
-
}
|
|
9180
|
-
function authTypeTag(tags) {
|
|
9181
|
-
const t = Array.isArray(tags) ? tags : [];
|
|
9182
|
-
const explicit = explicitAuthTypeTags(t);
|
|
9183
|
-
return explicit.length === 1 ? explicit[0] : void 0;
|
|
9568
|
+
if (url.origin !== PLACEHOLDER_ORIGIN)
|
|
9569
|
+
return ADMIN_VIEW_DEFAULT_RETURN_TO;
|
|
9570
|
+
if (BLOCKED_PATH_RE.test(url.pathname))
|
|
9571
|
+
return ADMIN_VIEW_DEFAULT_RETURN_TO;
|
|
9572
|
+
const out = `${url.pathname}${url.search}${url.hash}`;
|
|
9573
|
+
if (out.startsWith("//") || out.includes("\\"))
|
|
9574
|
+
return ADMIN_VIEW_DEFAULT_RETURN_TO;
|
|
9575
|
+
return out;
|
|
9184
9576
|
}
|
|
9185
|
-
var
|
|
9186
|
-
var
|
|
9187
|
-
"../shared/dist/
|
|
9577
|
+
var ADMIN_VIEW_DEFAULT_RETURN_TO, MAX_RETURN_TO_LENGTH, PLACEHOLDER_ORIGIN, BLOCKED_PATH_RE;
|
|
9578
|
+
var init_admin_view_return_to = __esm({
|
|
9579
|
+
"../shared/dist/admin-view-return-to.js"() {
|
|
9188
9580
|
"use strict";
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
AUTH_TAG_NONE = "@auth-none";
|
|
9194
|
-
AUTH_TAG_UNSUPPORTED = "@auth-unsupported";
|
|
9195
|
-
AUTH_TYPE_TAGS = [
|
|
9196
|
-
AUTH_TAG_COOKIE_STATE,
|
|
9197
|
-
AUTH_TAG_LOGIN_COOKIE,
|
|
9198
|
-
AUTH_TAG_LOGIN_BEARER,
|
|
9199
|
-
AUTH_TAG_STANDING,
|
|
9200
|
-
AUTH_TAG_NONE,
|
|
9201
|
-
AUTH_TAG_UNSUPPORTED
|
|
9202
|
-
];
|
|
9203
|
-
AUTH_TAG_ALIASES = {
|
|
9204
|
-
"@auth-cookie": AUTH_TAG_COOKIE_STATE,
|
|
9205
|
-
"@auth-bearer": AUTH_TAG_LOGIN_BEARER,
|
|
9206
|
-
"@auth-token": AUTH_TAG_LOGIN_BEARER,
|
|
9207
|
-
"@auth-api-key": AUTH_TAG_STANDING,
|
|
9208
|
-
"@auth-standing-header": AUTH_TAG_STANDING
|
|
9209
|
-
};
|
|
9210
|
-
TEST_USER_ENV_KEYS = ["TEST_USER_EMAIL", "TEST_USER_PASSWORD"];
|
|
9211
|
-
API_AUTH_HEADERS_ENV_KEY = "API_AUTH_HEADERS";
|
|
9581
|
+
ADMIN_VIEW_DEFAULT_RETURN_TO = "/sessions";
|
|
9582
|
+
MAX_RETURN_TO_LENGTH = 2048;
|
|
9583
|
+
PLACEHOLDER_ORIGIN = "https://admin-view.invalid";
|
|
9584
|
+
BLOCKED_PATH_RE = /^\/(?:login|admin-view)/i;
|
|
9212
9585
|
}
|
|
9213
9586
|
});
|
|
9214
9587
|
|
|
@@ -9219,13 +9592,16 @@ function stripEchoLines(text) {
|
|
|
9219
9592
|
function matchesAny(text, patterns) {
|
|
9220
9593
|
return patterns.some((p) => p.test(text));
|
|
9221
9594
|
}
|
|
9595
|
+
function isIntentionalNegativeAuthTest(input) {
|
|
9596
|
+
const meta = [input.testName, input.description].filter(Boolean).join(" ");
|
|
9597
|
+
return matchesAny(meta, INTENTIONAL_NEGATIVE_AUTH);
|
|
9598
|
+
}
|
|
9222
9599
|
function detectAuthBlock(input) {
|
|
9223
9600
|
const tags = input.tags ?? [];
|
|
9224
9601
|
const isAuthed = tags.some((t) => AUTHED_TAGS.includes(t));
|
|
9225
9602
|
if (!isAuthed)
|
|
9226
9603
|
return NO_DETECTION;
|
|
9227
|
-
|
|
9228
|
-
if (matchesAny(meta, INTENTIONAL_NEGATIVE_AUTH))
|
|
9604
|
+
if (isIntentionalNegativeAuthTest(input))
|
|
9229
9605
|
return NO_DETECTION;
|
|
9230
9606
|
const sources = [];
|
|
9231
9607
|
if (input.error)
|
|
@@ -9328,7 +9704,7 @@ var init_auth_block_detector = __esm({
|
|
|
9328
9704
|
/\b(reject|deny|denied|block(ed)?)\b[^\n]{0,20}\b(login|sign[\s-]?in|auth|credential|password)\b/i
|
|
9329
9705
|
];
|
|
9330
9706
|
ECHO_LINE_RE = /^\s*(?:(?:📋|🎭|🌐|⚙️?)|(?:stderr:\s*|stdout:\s*)?Locator:)/u;
|
|
9331
|
-
UNAUTHORIZED_TEXT_RE = /(?:return(?:s|ed|ing)?|respond(?:s|ed|ing)?|got|receiv(?:e|ed|ing)|gives?|sends?|yield(?:s|ed|ing)?|results?\s+in)\s+(?:a\s+|an\s+|the\s+)?(?:401|403)\b|\bstatus(?:\s+code)?\s
|
|
9707
|
+
UNAUTHORIZED_TEXT_RE = /(?:return(?:s|ed|ing)?|respond(?:s|ed|ing)?|got|receiv(?:e|ed|ing)|gives?|sends?|yield(?:s|ed|ing)?|results?\s+in)\s+(?:a\s+|an\s+|the\s+)?(?:401|403)\b|\bstatus(?:\s+code)?\s*(?::\s*)?(?:401|403)\b|\bHTTP\/[\d.]+\s+(?:401|403)\b|(?:401|403)['’]d\b|(?:[→⇒]|->)\s*(?:401|403)\b|(?:return(?:s|ed|ing)?|respond(?:s|ed|ing)?|got|receiv(?:e|ed|ing)|threw|throws?)\s+(?:a\s+|an\s+)?(?:unauthorized|forbidden)\b|\bnot\s+authenticated\b/i;
|
|
9332
9708
|
}
|
|
9333
9709
|
});
|
|
9334
9710
|
|
|
@@ -10269,12 +10645,70 @@ function isSharedAuthInjectEnabled(value = process.env.SHARED_AUTH_INJECT) {
|
|
|
10269
10645
|
function isAuthReadinessGuardEnabled(value = process.env.AUTH_READINESS_GUARD) {
|
|
10270
10646
|
return value?.trim().toLowerCase() !== "false";
|
|
10271
10647
|
}
|
|
10648
|
+
function visualBaselinesEnabled(projectId, raw = process.env.VISUAL_BASELINES_PROJECTS) {
|
|
10649
|
+
if (typeof projectId !== "string" || projectId.trim() === "")
|
|
10650
|
+
return false;
|
|
10651
|
+
const listed = (raw ?? "").split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
10652
|
+
return listed.includes("*") || listed.includes(projectId);
|
|
10653
|
+
}
|
|
10272
10654
|
var init_feature_flags = __esm({
|
|
10273
10655
|
"../shared/dist/feature-flags.js"() {
|
|
10274
10656
|
"use strict";
|
|
10275
10657
|
}
|
|
10276
10658
|
});
|
|
10277
10659
|
|
|
10660
|
+
// ../shared/dist/visual-compare-limits.js
|
|
10661
|
+
function isSafeMaskSelector(selector) {
|
|
10662
|
+
if (typeof selector !== "string")
|
|
10663
|
+
return false;
|
|
10664
|
+
if (selector.trim().length === 0)
|
|
10665
|
+
return false;
|
|
10666
|
+
if (selector.length > VISUAL_MASK_SELECTOR_MAX_LENGTH)
|
|
10667
|
+
return false;
|
|
10668
|
+
return !VISUAL_MASK_SELECTOR_FORBIDDEN.test(selector);
|
|
10669
|
+
}
|
|
10670
|
+
var VISUAL_MASK_SELECTORS_MAX, VISUAL_MASK_SELECTOR_MAX_LENGTH, VISUAL_MASK_SELECTOR_FORBIDDEN;
|
|
10671
|
+
var init_visual_compare_limits = __esm({
|
|
10672
|
+
"../shared/dist/visual-compare-limits.js"() {
|
|
10673
|
+
"use strict";
|
|
10674
|
+
VISUAL_MASK_SELECTORS_MAX = 50;
|
|
10675
|
+
VISUAL_MASK_SELECTOR_MAX_LENGTH = 300;
|
|
10676
|
+
VISUAL_MASK_SELECTOR_FORBIDDEN = /[{};@<\r\n]/;
|
|
10677
|
+
}
|
|
10678
|
+
});
|
|
10679
|
+
|
|
10680
|
+
// ../shared/dist/visual-baseline-identity.js
|
|
10681
|
+
function isBrowserVersion(value) {
|
|
10682
|
+
return typeof value === "string" && value.length <= 32 && BROWSER_VERSION.test(value);
|
|
10683
|
+
}
|
|
10684
|
+
function renderIdentity(flavour, version, image) {
|
|
10685
|
+
if (!/^[a-z][a-z-]{0,31}$/.test(flavour) || !/^[a-z][a-z-]{0,31}$/.test(image) || !isBrowserVersion(version))
|
|
10686
|
+
return null;
|
|
10687
|
+
const identity = `${flavour}/${version}/${image}`;
|
|
10688
|
+
return identity.length <= RENDER_IDENTITY_MAX_LENGTH ? identity : null;
|
|
10689
|
+
}
|
|
10690
|
+
function v1RenderIdentity(version) {
|
|
10691
|
+
return renderIdentity(VISUAL_V1_IDENTITY.flavour, version, VISUAL_V1_IDENTITY.image);
|
|
10692
|
+
}
|
|
10693
|
+
function parseRenderIdentity(value) {
|
|
10694
|
+
if (typeof value !== "string" || value.length > RENDER_IDENTITY_MAX_LENGTH)
|
|
10695
|
+
return null;
|
|
10696
|
+
const parts = value.split("/");
|
|
10697
|
+
if (parts.length !== 3)
|
|
10698
|
+
return null;
|
|
10699
|
+
const [flavour, version, image] = parts;
|
|
10700
|
+
return renderIdentity(flavour, version, image) === value ? { flavour, version, image } : null;
|
|
10701
|
+
}
|
|
10702
|
+
var VISUAL_V1_IDENTITY, RENDER_IDENTITY_MAX_LENGTH, BROWSER_VERSION;
|
|
10703
|
+
var init_visual_baseline_identity = __esm({
|
|
10704
|
+
"../shared/dist/visual-baseline-identity.js"() {
|
|
10705
|
+
"use strict";
|
|
10706
|
+
VISUAL_V1_IDENTITY = { platform: "linux", image: "runner-fast", flavour: "headless-shell" };
|
|
10707
|
+
RENDER_IDENTITY_MAX_LENGTH = 64;
|
|
10708
|
+
BROWSER_VERSION = /^\d{1,4}(\.\d{1,6}){0,3}$/;
|
|
10709
|
+
}
|
|
10710
|
+
});
|
|
10711
|
+
|
|
10278
10712
|
// ../shared/dist/analytics-url.js
|
|
10279
10713
|
function isSensitiveQueryKey(key) {
|
|
10280
10714
|
return SENSITIVE_QUERY_KEY_RE.test(key);
|
|
@@ -10628,14 +11062,9 @@ function isOpenCodeGoModelId(id) {
|
|
|
10628
11062
|
function hasOpenCodeGoPrefix(id) {
|
|
10629
11063
|
return (id?.trim() ?? "").startsWith(OPENCODE_GO_PREFIX);
|
|
10630
11064
|
}
|
|
10631
|
-
function hasOpenCodeGo2Prefix(id) {
|
|
10632
|
-
return (id?.trim() ?? "").startsWith(OPENCODE_GO_2_PREFIX);
|
|
10633
|
-
}
|
|
10634
11065
|
function openCodeGoWireModel(id) {
|
|
10635
11066
|
const trimmed = id?.trim() ?? "";
|
|
10636
|
-
|
|
10637
|
-
return null;
|
|
10638
|
-
return trimmed.slice((hasOpenCodeGo2Prefix(trimmed) ? OPENCODE_GO_2_PREFIX : OPENCODE_GO_PREFIX).length);
|
|
11067
|
+
return isOpenCodeGoModelId(trimmed) ? trimmed.slice(OPENCODE_GO_PREFIX.length) : null;
|
|
10639
11068
|
}
|
|
10640
11069
|
function isZaiModelId(id) {
|
|
10641
11070
|
const trimmed = id?.trim() ?? "";
|
|
@@ -10744,7 +11173,7 @@ function museCliContributorMode(env = process.env) {
|
|
|
10744
11173
|
const raw = (env.MUSE_CLI_CONTRIBUTOR_MODE ?? "").trim().toLowerCase();
|
|
10745
11174
|
return raw === "off" || raw === "false" || raw === "0" || raw === "no" ? "off" : "on";
|
|
10746
11175
|
}
|
|
10747
|
-
var CLI_ENGINE_MODEL_IDS, BROWSER_CLI_ENGINE_IDS, PIPELINE_STEPS, CLI_ENGINE_RULES, ENGINE_NAMES, OPENROUTER_PREFIX, OPENROUTER_MODEL_PATTERN, OPENCODE_GO_PREFIX,
|
|
11176
|
+
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, ZAI_PREFIX, ZAI_MODEL_PATTERN;
|
|
10748
11177
|
var init_engine_spec = __esm({
|
|
10749
11178
|
"../shared/dist/engine-spec.js"() {
|
|
10750
11179
|
"use strict";
|
|
@@ -10803,8 +11232,7 @@ var init_engine_spec = __esm({
|
|
|
10803
11232
|
OPENROUTER_PREFIX = "openrouter:";
|
|
10804
11233
|
OPENROUTER_MODEL_PATTERN = /^openrouter:[a-z0-9][a-z0-9._-]{0,40}\/[A-Za-z0-9._:-]{1,100}$/;
|
|
10805
11234
|
OPENCODE_GO_PREFIX = "opencode-go:";
|
|
10806
|
-
|
|
10807
|
-
OPENCODE_GO_MODEL_PATTERN = /^opencode-go(?:-2)?:[a-z0-9][a-z0-9._-]{0,100}$/;
|
|
11235
|
+
OPENCODE_GO_MODEL_PATTERN = /^opencode-go:[a-z0-9][a-z0-9._-]{0,100}$/;
|
|
10808
11236
|
ZAI_PREFIX = "zai:";
|
|
10809
11237
|
ZAI_MODEL_PATTERN = /^zai:glm-[a-z0-9][a-z0-9._-]{0,60}$/;
|
|
10810
11238
|
}
|
|
@@ -11129,14 +11557,19 @@ function has(map2, key) {
|
|
|
11129
11557
|
}
|
|
11130
11558
|
function providerChainFor(modelId) {
|
|
11131
11559
|
const key = chainKey(modelId);
|
|
11560
|
+
if (key && has(_overrideHops, key))
|
|
11561
|
+
return _overrideHops[key];
|
|
11132
11562
|
return key && has(PROVIDER_CHAINS, key) ? PROVIDER_CHAINS[key] : [];
|
|
11133
11563
|
}
|
|
11134
11564
|
function providerChainLeadsFor(modelId) {
|
|
11135
11565
|
const key = chainKey(modelId);
|
|
11566
|
+
if (key && has(_overrideLeads, key))
|
|
11567
|
+
return _overrideLeads[key];
|
|
11136
11568
|
return key && has(PROVIDER_CHAIN_LEADS, key) ? PROVIDER_CHAIN_LEADS[key] : [];
|
|
11137
11569
|
}
|
|
11138
11570
|
function allChains() {
|
|
11139
|
-
|
|
11571
|
+
const ordered = (map2, override) => [...Object.entries(map2).filter(([key]) => !has(override, key)), ...Object.entries(override)];
|
|
11572
|
+
return [...ordered(PROVIDER_CHAINS, _overrideHops), ...ordered(PROVIDER_CHAIN_LEADS, _overrideLeads)];
|
|
11140
11573
|
}
|
|
11141
11574
|
function isProviderChainHopId(modelId) {
|
|
11142
11575
|
const id = modelId ?? "";
|
|
@@ -11153,14 +11586,24 @@ function providerChainHostOf(value, modelValue) {
|
|
|
11153
11586
|
return "own";
|
|
11154
11587
|
if (hasZaiPrefix(value))
|
|
11155
11588
|
return "zai";
|
|
11156
|
-
if (hasOpenCodeGo2Prefix(value))
|
|
11157
|
-
return "opencode-go-2";
|
|
11158
11589
|
if (hasOpenCodeGoPrefix(value))
|
|
11159
11590
|
return "opencode-go";
|
|
11160
11591
|
if (hasOpenRouterPrefix(value))
|
|
11161
11592
|
return "openrouter";
|
|
11162
11593
|
return "own";
|
|
11163
11594
|
}
|
|
11595
|
+
function providerChainHostProvider(value) {
|
|
11596
|
+
if (typeof value !== "string" || !value)
|
|
11597
|
+
return null;
|
|
11598
|
+
if (hasOpenRouterPrefix(value))
|
|
11599
|
+
return "openrouter";
|
|
11600
|
+
if (hasOpenCodeGoPrefix(value))
|
|
11601
|
+
return "opencode-go";
|
|
11602
|
+
if (hasZaiPrefix(value))
|
|
11603
|
+
return "zai";
|
|
11604
|
+
const key = chainKey(value);
|
|
11605
|
+
return API_MODEL_CATALOG.find((m) => m.id === key)?.provider ?? GEMINI_ASSISTANT_MODELS.find((m) => m.id === key)?.provider ?? null;
|
|
11606
|
+
}
|
|
11164
11607
|
function providerHostPlan(modelId) {
|
|
11165
11608
|
const key = chainKey(modelId);
|
|
11166
11609
|
if (!key)
|
|
@@ -11178,7 +11621,140 @@ function providerHostPlan(modelId) {
|
|
|
11178
11621
|
}
|
|
11179
11622
|
return steps;
|
|
11180
11623
|
}
|
|
11181
|
-
|
|
11624
|
+
function providerChainModelKey(modelId) {
|
|
11625
|
+
return chainKey(modelId);
|
|
11626
|
+
}
|
|
11627
|
+
function hasProviderChainLeadsByDefault(modelId) {
|
|
11628
|
+
const key = chainKey(modelId);
|
|
11629
|
+
return key !== null && has(PROVIDER_CHAIN_LEADS, key);
|
|
11630
|
+
}
|
|
11631
|
+
function isChainablePrimary(id) {
|
|
11632
|
+
return typeof id === "string" && id !== "" && (has(PROVIDER_CHAINS, id) || has(PROVIDER_CHAIN_LEADS, id));
|
|
11633
|
+
}
|
|
11634
|
+
function defaultProviderChainEntries(primary) {
|
|
11635
|
+
if (!isChainablePrimary(primary))
|
|
11636
|
+
return [];
|
|
11637
|
+
const leads = has(PROVIDER_CHAIN_LEADS, primary) ? PROVIDER_CHAIN_LEADS[primary] : [];
|
|
11638
|
+
const hops = has(PROVIDER_CHAINS, primary) ? PROVIDER_CHAINS[primary] : [];
|
|
11639
|
+
return [...leads, primary, ...hops].map((hop) => ({ hop, enabled: true }));
|
|
11640
|
+
}
|
|
11641
|
+
function allowedProviderChainHosts(primary) {
|
|
11642
|
+
return defaultProviderChainEntries(primary).map((entry) => entry.hop);
|
|
11643
|
+
}
|
|
11644
|
+
function isPlainObject2(value) {
|
|
11645
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
11646
|
+
}
|
|
11647
|
+
function validateProviderChainEntries(primary, entries) {
|
|
11648
|
+
if (!isChainablePrimary(primary))
|
|
11649
|
+
return `${String(primary).slice(0, 80)} has no provider chain to order`;
|
|
11650
|
+
if (!Array.isArray(entries))
|
|
11651
|
+
return `${primary}: the chain must be a list`;
|
|
11652
|
+
if (entries.length > PROVIDER_CHAIN_MAX_ENTRIES)
|
|
11653
|
+
return `${primary}: at most ${PROVIDER_CHAIN_MAX_ENTRIES} hosts`;
|
|
11654
|
+
const allowed = allowedProviderChainHosts(primary);
|
|
11655
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11656
|
+
for (const entry of entries) {
|
|
11657
|
+
if (!isPlainObject2(entry))
|
|
11658
|
+
return `${primary}: each host must be { hop, enabled }`;
|
|
11659
|
+
const keys = Object.keys(entry);
|
|
11660
|
+
if (keys.length !== 2 || !keys.includes("hop") || !keys.includes("enabled"))
|
|
11661
|
+
return `${primary}: each host must be { hop, enabled } and nothing else`;
|
|
11662
|
+
const { hop, enabled } = entry;
|
|
11663
|
+
if (typeof enabled !== "boolean")
|
|
11664
|
+
return `${primary}: enabled must be true or false`;
|
|
11665
|
+
if (typeof hop !== "string" || !hop)
|
|
11666
|
+
return `${primary}: hop must be a model value`;
|
|
11667
|
+
if (!allowed.includes(hop))
|
|
11668
|
+
return `${primary}: ${hop.slice(0, 80)} is not a host of this model`;
|
|
11669
|
+
if (seen.has(hop))
|
|
11670
|
+
return `${primary}: ${hop} is listed twice`;
|
|
11671
|
+
if (hop === primary && !enabled)
|
|
11672
|
+
return `${primary}: the model's own value stays on (pick another model on AI Models instead)`;
|
|
11673
|
+
seen.add(hop);
|
|
11674
|
+
}
|
|
11675
|
+
if (!seen.has(primary))
|
|
11676
|
+
return `${primary}: list the model's own value once, where its own host sits`;
|
|
11677
|
+
return null;
|
|
11678
|
+
}
|
|
11679
|
+
function decodeProviderChains(raw) {
|
|
11680
|
+
let value = raw;
|
|
11681
|
+
if (typeof raw === "string") {
|
|
11682
|
+
if (raw.length > PROVIDER_CHAINS_SETTING_MAX_CHARS)
|
|
11683
|
+
return { ok: false, error: `AI_PROVIDER_CHAINS is too long (at most ${PROVIDER_CHAINS_SETTING_MAX_CHARS} characters)` };
|
|
11684
|
+
try {
|
|
11685
|
+
value = JSON.parse(raw);
|
|
11686
|
+
} catch {
|
|
11687
|
+
return { ok: false, error: "AI_PROVIDER_CHAINS must be valid JSON" };
|
|
11688
|
+
}
|
|
11689
|
+
} else if (JSON.stringify(raw ?? null).length > PROVIDER_CHAINS_SETTING_MAX_CHARS) {
|
|
11690
|
+
return { ok: false, error: `AI_PROVIDER_CHAINS is too long (at most ${PROVIDER_CHAINS_SETTING_MAX_CHARS} characters)` };
|
|
11691
|
+
}
|
|
11692
|
+
if (!isPlainObject2(value))
|
|
11693
|
+
return { ok: false, error: 'AI_PROVIDER_CHAINS must be an object: { "<model>": [ { "hop": "<model value>", "enabled": true } ] }' };
|
|
11694
|
+
if (Object.keys(value).length > PROVIDER_CHAIN_MAX_PRIMARIES)
|
|
11695
|
+
return { ok: false, error: `AI_PROVIDER_CHAINS names at most ${PROVIDER_CHAIN_MAX_PRIMARIES} models` };
|
|
11696
|
+
return { ok: true, value };
|
|
11697
|
+
}
|
|
11698
|
+
function checkedProviderChains(value) {
|
|
11699
|
+
const out = {};
|
|
11700
|
+
for (const [primary, entries] of Object.entries(value)) {
|
|
11701
|
+
const problem = validateProviderChainEntries(primary, entries);
|
|
11702
|
+
if (problem)
|
|
11703
|
+
return { ok: false, error: problem };
|
|
11704
|
+
out[primary] = entries.map(({ hop, enabled }) => ({ hop, enabled }));
|
|
11705
|
+
}
|
|
11706
|
+
return { ok: true, value: out };
|
|
11707
|
+
}
|
|
11708
|
+
function parseProviderChainOverrides(raw) {
|
|
11709
|
+
const decoded = decodeProviderChains(raw);
|
|
11710
|
+
return decoded.ok ? checkedProviderChains(decoded.value) : decoded;
|
|
11711
|
+
}
|
|
11712
|
+
function parseStoredProviderChainOverrides(raw) {
|
|
11713
|
+
const decoded = decodeProviderChains(raw);
|
|
11714
|
+
if (!decoded.ok)
|
|
11715
|
+
return decoded;
|
|
11716
|
+
const dropped = [];
|
|
11717
|
+
const kept = {};
|
|
11718
|
+
for (const [primary, entries] of Object.entries(decoded.value)) {
|
|
11719
|
+
if (!isChainablePrimary(primary) || !Array.isArray(entries)) {
|
|
11720
|
+
kept[primary] = entries;
|
|
11721
|
+
continue;
|
|
11722
|
+
}
|
|
11723
|
+
const allowed = allowedProviderChainHosts(primary);
|
|
11724
|
+
kept[primary] = entries.filter((entry) => {
|
|
11725
|
+
const hop = isPlainObject2(entry) ? entry.hop : void 0;
|
|
11726
|
+
if (typeof hop !== "string" || hop === "" || allowed.includes(hop))
|
|
11727
|
+
return true;
|
|
11728
|
+
dropped.push({ primary, hop: hop.slice(0, 80) });
|
|
11729
|
+
return false;
|
|
11730
|
+
});
|
|
11731
|
+
}
|
|
11732
|
+
const checked = checkedProviderChains(kept);
|
|
11733
|
+
return checked.ok ? { ...checked, dropped } : checked;
|
|
11734
|
+
}
|
|
11735
|
+
function validateProviderChainsSetting(raw) {
|
|
11736
|
+
const parsed = parseProviderChainOverrides(raw);
|
|
11737
|
+
return parsed.ok ? null : parsed.error;
|
|
11738
|
+
}
|
|
11739
|
+
function setProviderChainOverrides(value, onDropped) {
|
|
11740
|
+
const parsed = value === null || value === void 0 || value === "" ? null : parseStoredProviderChainOverrides(value);
|
|
11741
|
+
if (parsed?.ok && parsed.dropped.length > 0)
|
|
11742
|
+
onDropped?.(parsed.dropped);
|
|
11743
|
+
_overrides = parsed?.ok ? parsed.value : null;
|
|
11744
|
+
_overrideLeads = {};
|
|
11745
|
+
_overrideHops = {};
|
|
11746
|
+
for (const [primary, entries] of Object.entries(_overrides ?? {})) {
|
|
11747
|
+
const at = entries.findIndex((entry) => entry.hop === primary);
|
|
11748
|
+
const on = (list) => list.filter((entry) => entry.enabled).map((entry) => entry.hop);
|
|
11749
|
+
_overrideLeads[primary] = on(entries.slice(0, at));
|
|
11750
|
+
_overrideHops[primary] = on(entries.slice(at + 1));
|
|
11751
|
+
}
|
|
11752
|
+
return parsed === null || parsed.ok;
|
|
11753
|
+
}
|
|
11754
|
+
function getProviderChainOverrides() {
|
|
11755
|
+
return _overrides;
|
|
11756
|
+
}
|
|
11757
|
+
var PROVIDER_CHAINS, PROVIDER_CHAIN_LEADS, PROVIDER_HOST_NOT_OFFERED, PROVIDER_CHAIN_MAX_ENTRIES, PROVIDER_CHAINS_SETTING_MAX_CHARS, PROVIDER_CHAIN_MAX_PRIMARIES, ALLOWED_PROVIDER_CHAIN_HOPS, _overrides, _overrideLeads, _overrideHops;
|
|
11182
11758
|
var init_provider_chains = __esm({
|
|
11183
11759
|
"../shared/dist/provider-chains.js"() {
|
|
11184
11760
|
"use strict";
|
|
@@ -11190,18 +11766,120 @@ var init_provider_chains = __esm({
|
|
|
11190
11766
|
"muse-spark-1.3-contributor": ["openrouter:meta/muse-spark-1.3-contributor"],
|
|
11191
11767
|
"claude-haiku-5-5": ["opencode-go:claude-haiku-5-5", "openrouter:anthropic/claude-haiku-5.5"],
|
|
11192
11768
|
"gemini-3.1-pro-preview": ["openrouter:google/gemini-3.1-pro-preview"],
|
|
11193
|
-
"deepseek-flash": ["opencode-go
|
|
11194
|
-
"deepseek-v4-pro": ["opencode-go
|
|
11769
|
+
"deepseek-flash": ["opencode-go:deepseek-v4.1-flash", "openrouter:deepseek/deepseek-v4.1-flash"],
|
|
11770
|
+
"deepseek-v4-pro": ["opencode-go:deepseek-v4-pro", "openrouter:deepseek/deepseek-v4-pro"]
|
|
11195
11771
|
};
|
|
11196
11772
|
PROVIDER_CHAIN_LEADS = {
|
|
11197
|
-
"openrouter:z-ai/glm-5.3-flash": ["zai:glm-5.3-flash", "opencode-go
|
|
11198
|
-
"openrouter:z-ai/glm-5.3": ["zai:glm-5.3", "opencode-go
|
|
11199
|
-
"openrouter:z-ai/glm-5.2": ["zai:glm-5.2", "opencode-go
|
|
11773
|
+
"openrouter:z-ai/glm-5.3-flash": ["zai:glm-5.3-flash", "opencode-go:glm-5.3-flash"],
|
|
11774
|
+
"openrouter:z-ai/glm-5.3": ["zai:glm-5.3", "opencode-go:glm-5.3"],
|
|
11775
|
+
"openrouter:z-ai/glm-5.2": ["zai:glm-5.2", "opencode-go:glm-5.2"]
|
|
11200
11776
|
};
|
|
11201
11777
|
PROVIDER_HOST_NOT_OFFERED = {
|
|
11202
11778
|
"muse-spark-1.3-contributor": { "opencode-go": "OpenCode Go serves Muse only on its Responses API" },
|
|
11203
11779
|
"gemini-3.1-pro-preview": { "opencode-go": "OpenCode Go does not serve Gemini" }
|
|
11204
11780
|
};
|
|
11781
|
+
PROVIDER_CHAIN_MAX_ENTRIES = 8;
|
|
11782
|
+
PROVIDER_CHAINS_SETTING_MAX_CHARS = 16e3;
|
|
11783
|
+
PROVIDER_CHAIN_MAX_PRIMARIES = 32;
|
|
11784
|
+
ALLOWED_PROVIDER_CHAIN_HOPS = /* @__PURE__ */ new Set([
|
|
11785
|
+
...Object.values(PROVIDER_CHAINS).flat(),
|
|
11786
|
+
...Object.values(PROVIDER_CHAIN_LEADS).flat()
|
|
11787
|
+
]);
|
|
11788
|
+
_overrides = null;
|
|
11789
|
+
_overrideLeads = {};
|
|
11790
|
+
_overrideHops = {};
|
|
11791
|
+
}
|
|
11792
|
+
});
|
|
11793
|
+
|
|
11794
|
+
// ../shared/dist/provider-accounts.js
|
|
11795
|
+
function modelNames(modelId) {
|
|
11796
|
+
const value = modelId.trim();
|
|
11797
|
+
const names = /* @__PURE__ */ new Set([value]);
|
|
11798
|
+
for (const prefix of HOST_PREFIXES) {
|
|
11799
|
+
if (!value.startsWith(prefix))
|
|
11800
|
+
continue;
|
|
11801
|
+
const bare = value.slice(prefix.length);
|
|
11802
|
+
names.add(bare);
|
|
11803
|
+
const slash = bare.lastIndexOf("/");
|
|
11804
|
+
if (slash >= 0)
|
|
11805
|
+
names.add(bare.slice(slash + 1));
|
|
11806
|
+
}
|
|
11807
|
+
const canonical = canonicalModelId(value);
|
|
11808
|
+
if (canonical) {
|
|
11809
|
+
names.add(canonical);
|
|
11810
|
+
const entry = API_MODEL_CATALOG.find((m) => m.id === canonical || m.apiModel === canonical);
|
|
11811
|
+
if (entry) {
|
|
11812
|
+
names.add(entry.id);
|
|
11813
|
+
if (entry.apiModel)
|
|
11814
|
+
names.add(entry.apiModel);
|
|
11815
|
+
}
|
|
11816
|
+
}
|
|
11817
|
+
return names;
|
|
11818
|
+
}
|
|
11819
|
+
function accountServesModel(models, modelId) {
|
|
11820
|
+
if (!models || models.length === 0)
|
|
11821
|
+
return true;
|
|
11822
|
+
if (!modelId)
|
|
11823
|
+
return false;
|
|
11824
|
+
const names = modelNames(modelId);
|
|
11825
|
+
return models.some((model) => names.has(model.trim()));
|
|
11826
|
+
}
|
|
11827
|
+
function normalizeProviderAccountModels(models) {
|
|
11828
|
+
if (!Array.isArray(models))
|
|
11829
|
+
return null;
|
|
11830
|
+
const out = [];
|
|
11831
|
+
for (const model of models) {
|
|
11832
|
+
if (typeof model !== "string")
|
|
11833
|
+
return null;
|
|
11834
|
+
const trimmed = model.trim();
|
|
11835
|
+
if (!PROVIDER_ACCOUNT_MODEL_PATTERN.test(trimmed) || trimmed.includes(".."))
|
|
11836
|
+
return null;
|
|
11837
|
+
if (!out.includes(trimmed))
|
|
11838
|
+
out.push(trimmed);
|
|
11839
|
+
}
|
|
11840
|
+
return out.length > MAX_PROVIDER_ACCOUNT_MODELS ? null : out;
|
|
11841
|
+
}
|
|
11842
|
+
function parseProviderAccountLists(value, providers) {
|
|
11843
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
11844
|
+
return null;
|
|
11845
|
+
const parsed = {};
|
|
11846
|
+
for (const provider of providers) {
|
|
11847
|
+
const list = value[provider];
|
|
11848
|
+
if (!Array.isArray(list))
|
|
11849
|
+
continue;
|
|
11850
|
+
const accounts = [];
|
|
11851
|
+
for (const raw of list) {
|
|
11852
|
+
if (!raw || typeof raw !== "object")
|
|
11853
|
+
continue;
|
|
11854
|
+
const { id, label, key, models } = raw;
|
|
11855
|
+
if (typeof id !== "string" || !id.trim() || typeof key !== "string" || !key.trim())
|
|
11856
|
+
continue;
|
|
11857
|
+
const modelList = Array.isArray(models) ? models.filter((m) => typeof m === "string" && m.trim() !== "").map((m) => m.trim()) : [];
|
|
11858
|
+
accounts.push({ id: id.trim(), label: typeof label === "string" && label.trim() ? label.trim() : null, key: key.trim(), models: modelList });
|
|
11859
|
+
}
|
|
11860
|
+
if (accounts.length > 0)
|
|
11861
|
+
parsed[provider] = accounts;
|
|
11862
|
+
}
|
|
11863
|
+
return parsed;
|
|
11864
|
+
}
|
|
11865
|
+
var DEFAULT_PROVIDER_ACCOUNT_ID, PROVIDER_ACCOUNT_ID_PATTERN, MAX_PROVIDER_ACCOUNTS, MAX_PROVIDER_ACCOUNT_MODELS, PROVIDER_ACCOUNT_MODEL_PATTERN, OPENCODE_GO_DEEPSEEK_GLM_MODELS, HOST_PREFIXES;
|
|
11866
|
+
var init_provider_accounts = __esm({
|
|
11867
|
+
"../shared/dist/provider-accounts.js"() {
|
|
11868
|
+
"use strict";
|
|
11869
|
+
init_api_model_catalog();
|
|
11870
|
+
DEFAULT_PROVIDER_ACCOUNT_ID = "primary";
|
|
11871
|
+
PROVIDER_ACCOUNT_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,39}$/;
|
|
11872
|
+
MAX_PROVIDER_ACCOUNTS = 10;
|
|
11873
|
+
MAX_PROVIDER_ACCOUNT_MODELS = 50;
|
|
11874
|
+
PROVIDER_ACCOUNT_MODEL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,120}$/;
|
|
11875
|
+
OPENCODE_GO_DEEPSEEK_GLM_MODELS = [
|
|
11876
|
+
"deepseek-v4.1-flash",
|
|
11877
|
+
"deepseek-v4-pro",
|
|
11878
|
+
"glm-5.3-flash",
|
|
11879
|
+
"glm-5.3",
|
|
11880
|
+
"glm-5.2"
|
|
11881
|
+
];
|
|
11882
|
+
HOST_PREFIXES = ["opencode-go:", "zai:", "openrouter:"];
|
|
11205
11883
|
}
|
|
11206
11884
|
});
|
|
11207
11885
|
|
|
@@ -11320,6 +11998,25 @@ var init_project_engine_overrides = __esm({
|
|
|
11320
11998
|
}
|
|
11321
11999
|
});
|
|
11322
12000
|
|
|
12001
|
+
// ../shared/dist/trailing-slashes.js
|
|
12002
|
+
function stripTrailingSlashes(value) {
|
|
12003
|
+
let end = value.length;
|
|
12004
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47)
|
|
12005
|
+
end--;
|
|
12006
|
+
return end === value.length ? value : value.slice(0, end);
|
|
12007
|
+
}
|
|
12008
|
+
function stripTrailingChars(value, isTrailing) {
|
|
12009
|
+
let end = value.length;
|
|
12010
|
+
while (end > 0 && isTrailing.test(value[end - 1]))
|
|
12011
|
+
end--;
|
|
12012
|
+
return end === value.length ? value : value.slice(0, end);
|
|
12013
|
+
}
|
|
12014
|
+
var init_trailing_slashes = __esm({
|
|
12015
|
+
"../shared/dist/trailing-slashes.js"() {
|
|
12016
|
+
"use strict";
|
|
12017
|
+
}
|
|
12018
|
+
});
|
|
12019
|
+
|
|
11323
12020
|
// ../shared/dist/gemini-visual.js
|
|
11324
12021
|
function visualProDailyBudget(raw) {
|
|
11325
12022
|
const parsed = raw && /^\s*\d{1,6}\s*$/.test(raw) ? Number(raw.trim()) : NaN;
|
|
@@ -11333,7 +12030,7 @@ function isRetiredGeminiModel(id) {
|
|
|
11333
12030
|
}
|
|
11334
12031
|
function geminiNativeModelsUrl(base2 = process.env.GEMINI_API_BASE) {
|
|
11335
12032
|
const raw = base2 && base2.trim() || GEMINI_OPENAI_BASE;
|
|
11336
|
-
const native = raw.replace(/\/openai\/?$/, "/")
|
|
12033
|
+
const native = stripTrailingSlashes(raw.replace(/\/openai\/?$/, "/"));
|
|
11337
12034
|
return `${native}/models?pageSize=1000`;
|
|
11338
12035
|
}
|
|
11339
12036
|
function asRecord(value) {
|
|
@@ -11453,6 +12150,7 @@ var KNOWN_INPUT_USD_PER_M, GEMINI_OPENAI_BASE, DEFAULT_GEMINI_SCREEN_MODEL, DEFA
|
|
|
11453
12150
|
var init_gemini_visual = __esm({
|
|
11454
12151
|
"../shared/dist/gemini-visual.js"() {
|
|
11455
12152
|
"use strict";
|
|
12153
|
+
init_trailing_slashes();
|
|
11456
12154
|
KNOWN_INPUT_USD_PER_M = {
|
|
11457
12155
|
"gemini-2.5-flash-lite": 0.1,
|
|
11458
12156
|
"gemini-3.1-flash-lite": 0.25,
|
|
@@ -12816,12 +13514,13 @@ function truncateAtWord(text, max) {
|
|
|
12816
13514
|
const slice = trimmed.slice(0, room + 1);
|
|
12817
13515
|
const lastSpace = slice.lastIndexOf(" ");
|
|
12818
13516
|
const cut = lastSpace > room * 0.5 ? slice.slice(0, lastSpace) : trimmed.slice(0, room);
|
|
12819
|
-
return `${cut
|
|
13517
|
+
return `${stripTrailingChars(cut, TRAILING_PUNCTUATION)}\u2026`;
|
|
12820
13518
|
}
|
|
12821
|
-
var CANVAS_UNSUPPORTED_CODE, CANVAS_UNSUPPORTED_TITLE, CANVAS_UNSUPPORTED_BODY, CANVAS_UNSUPPORTED_NOT_CHARGED, CANVAS_UNSUPPORTED_OVERRIDE_HINT, GAME_ENGINES, RENDERER_ENGINES, ENGINE_TEXT_SIGNATURES, GAME_LIBRARY_TEXT_PATTERNS, ENGINE_PACKAGES, GAME_HINT_PACKAGES, MAX_TAG_CHARS, MAX_CSS_SELECTOR_CHARS, MAX_CSS_BLOCK_CHARS, TAG_NAME_RE, ATTR_RE, INTERACTIVE_TAGS, INTERACTIVE_ROLES, ROOT_SELECTOR_RE, OVERFLOW_HIDDEN_RE, TOUCH_NONE_RE, CODE_TAGS, STATIC_STANDARD_MIN_INTERACTIVE;
|
|
13519
|
+
var CANVAS_UNSUPPORTED_CODE, CANVAS_UNSUPPORTED_TITLE, CANVAS_UNSUPPORTED_BODY, CANVAS_UNSUPPORTED_NOT_CHARGED, CANVAS_UNSUPPORTED_OVERRIDE_HINT, GAME_ENGINES, RENDERER_ENGINES, ENGINE_TEXT_SIGNATURES, GAME_LIBRARY_TEXT_PATTERNS, ENGINE_PACKAGES, GAME_HINT_PACKAGES, MAX_TAG_CHARS, MAX_CSS_SELECTOR_CHARS, MAX_CSS_BLOCK_CHARS, TAG_NAME_RE, ATTR_RE, INTERACTIVE_TAGS, INTERACTIVE_ROLES, ROOT_SELECTOR_RE, OVERFLOW_HIDDEN_RE, TOUCH_NONE_RE, CODE_TAGS, STATIC_STANDARD_MIN_INTERACTIVE, TRAILING_PUNCTUATION;
|
|
12822
13520
|
var init_canvas_support = __esm({
|
|
12823
13521
|
"../shared/dist/canvas-support.js"() {
|
|
12824
13522
|
"use strict";
|
|
13523
|
+
init_trailing_slashes();
|
|
12825
13524
|
CANVAS_UNSUPPORTED_CODE = "canvas_unsupported";
|
|
12826
13525
|
CANVAS_UNSUPPORTED_TITLE = "Games and canvas apps aren't supported yet";
|
|
12827
13526
|
CANVAS_UNSUPPORTED_BODY = "This app draws its screen on a canvas (like a game), so there are no page elements for Validate.QA to read and test. Support for games and canvas apps is coming.";
|
|
@@ -12946,6 +13645,7 @@ var init_canvas_support = __esm({
|
|
|
12946
13645
|
TOUCH_NONE_RE = /touch-action\s{0,8}:\s{0,8}none/;
|
|
12947
13646
|
CODE_TAGS = /* @__PURE__ */ new Set(["script", "link", "flt-glass-pane", "flutter-view", "flt-scene-host"]);
|
|
12948
13647
|
STATIC_STANDARD_MIN_INTERACTIVE = 3;
|
|
13648
|
+
TRAILING_PUNCTUATION = /[\s,;:.\-–—]/u;
|
|
12949
13649
|
}
|
|
12950
13650
|
});
|
|
12951
13651
|
|
|
@@ -13128,22 +13828,22 @@ function joinChainedLines(code) {
|
|
|
13128
13828
|
const trimmed = lines[i].trimStart();
|
|
13129
13829
|
if (last !== -1 && trimmed.startsWith(".") && !trimmed.startsWith("...")) {
|
|
13130
13830
|
if (!lastStripped) {
|
|
13131
|
-
lines[last] =
|
|
13831
|
+
lines[last] = stripLineComment3(lines[last]).trimEnd();
|
|
13132
13832
|
lastStripped = true;
|
|
13133
13833
|
}
|
|
13134
13834
|
if (lines[last].length <= MAX_LINE_CHARS)
|
|
13135
|
-
lines[last] = `${lines[last]} ${
|
|
13835
|
+
lines[last] = `${lines[last]} ${stripLineComment3(trimmed).trimEnd()}`;
|
|
13136
13836
|
lines[i] = "";
|
|
13137
13837
|
continue;
|
|
13138
13838
|
}
|
|
13139
|
-
if (
|
|
13839
|
+
if (stripLineComment3(lines[i]).trim()) {
|
|
13140
13840
|
last = i;
|
|
13141
13841
|
lastStripped = false;
|
|
13142
13842
|
}
|
|
13143
13843
|
}
|
|
13144
13844
|
return lines.join("\n");
|
|
13145
13845
|
}
|
|
13146
|
-
function
|
|
13846
|
+
function stripLineComment3(rawLine) {
|
|
13147
13847
|
if (rawLine.trimStart().startsWith("//"))
|
|
13148
13848
|
return "";
|
|
13149
13849
|
const at = rawLine.indexOf(" // ");
|
|
@@ -13192,7 +13892,7 @@ function analyzeReadinessPolling(rawCode) {
|
|
|
13192
13892
|
offset += rawLine.length + 1;
|
|
13193
13893
|
if (rawLine.length > MAX_LINE_CHARS)
|
|
13194
13894
|
continue;
|
|
13195
|
-
const line =
|
|
13895
|
+
const line = stripLineComment3(rawLine);
|
|
13196
13896
|
if (!line.trim())
|
|
13197
13897
|
continue;
|
|
13198
13898
|
const literals = literalsOf(line);
|
|
@@ -13304,7 +14004,7 @@ function blankBlockComments2(code) {
|
|
|
13304
14004
|
from = close + 2;
|
|
13305
14005
|
}
|
|
13306
14006
|
}
|
|
13307
|
-
function
|
|
14007
|
+
function stripLineComment4(rawLine) {
|
|
13308
14008
|
if (rawLine.trimStart().startsWith("//"))
|
|
13309
14009
|
return "";
|
|
13310
14010
|
const at = rawLine.indexOf(" // ");
|
|
@@ -13315,7 +14015,7 @@ function logicalLines(code) {
|
|
|
13315
14015
|
for (const raw of code.split("\n")) {
|
|
13316
14016
|
if (raw.length > MAX_LINE_CHARS2)
|
|
13317
14017
|
continue;
|
|
13318
|
-
const line =
|
|
14018
|
+
const line = stripLineComment4(raw).trimEnd();
|
|
13319
14019
|
if (!line.trim())
|
|
13320
14020
|
continue;
|
|
13321
14021
|
const trimmed = line.trimStart();
|
|
@@ -13677,8 +14377,19 @@ function short4(text, max) {
|
|
|
13677
14377
|
const flat = text.replace(/\s+/g, " ").trim();
|
|
13678
14378
|
return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
|
|
13679
14379
|
}
|
|
14380
|
+
function stripFractionZeros(s) {
|
|
14381
|
+
let end = s.length;
|
|
14382
|
+
while (end > 0 && s[end - 1] === "0")
|
|
14383
|
+
end--;
|
|
14384
|
+
if (end === s.length)
|
|
14385
|
+
return s;
|
|
14386
|
+
const dot = s.lastIndexOf(".");
|
|
14387
|
+
if (dot < 0)
|
|
14388
|
+
return s;
|
|
14389
|
+
return s.slice(0, end === dot + 1 ? dot : end);
|
|
14390
|
+
}
|
|
13680
14391
|
function digitsOf(token) {
|
|
13681
|
-
return token.replace(/[^\d.]/g, "")
|
|
14392
|
+
return stripFractionZeros(token.replace(/[^\d.]/g, "")).replace(/^0+(?=\d)/, "");
|
|
13682
14393
|
}
|
|
13683
14394
|
function computes(line) {
|
|
13684
14395
|
const code = line.replace(STRING_RE, '""').replace(REGEX_LITERAL_RE, "$1RX").replace(LITERAL_ARITH_RE, "N");
|
|
@@ -13896,7 +14607,7 @@ function findExpectCalls(code) {
|
|
|
13896
14607
|
if (!subj)
|
|
13897
14608
|
continue;
|
|
13898
14609
|
const rest = code.slice(subj.end + 1, subj.end + 1 + 80);
|
|
13899
|
-
const mm = /^\s*((?:\.\s*not)
|
|
14610
|
+
const mm = /^\s*((?:\.\s*not\s*)?\.\s*(\w+))\s{0,20}\(/.exec(rest);
|
|
13900
14611
|
if (!mm)
|
|
13901
14612
|
continue;
|
|
13902
14613
|
const argStart = subj.end + 1 + mm[0].length;
|
|
@@ -15846,6 +16557,28 @@ function startsWithRegexLiteral(text) {
|
|
|
15846
16557
|
const t = text.trimStart();
|
|
15847
16558
|
return t[0] === "/" && t[1] !== "/" && t[1] !== "*" && regexLiteralEnd4(t, 0) !== -1;
|
|
15848
16559
|
}
|
|
16560
|
+
function isBareRegexLiteral(text) {
|
|
16561
|
+
const t = text.trimStart();
|
|
16562
|
+
if (t[0] !== "/" || t[1] === "/" || t[1] === "*")
|
|
16563
|
+
return false;
|
|
16564
|
+
const end = regexLiteralEnd4(t, 0);
|
|
16565
|
+
if (end === -1)
|
|
16566
|
+
return false;
|
|
16567
|
+
let i = end;
|
|
16568
|
+
while (i < t.length && /[a-z]/.test(t[i]))
|
|
16569
|
+
i++;
|
|
16570
|
+
while (i < t.length && /\s/.test(t[i]))
|
|
16571
|
+
i++;
|
|
16572
|
+
return i >= t.length || t[i] === ",";
|
|
16573
|
+
}
|
|
16574
|
+
function regexDeclaredNames(code) {
|
|
16575
|
+
const names = /* @__PURE__ */ new Set();
|
|
16576
|
+
const decl = /\b(?:const|let|var)\s{1,20}([A-Za-z_$][\w$]{0,80})\s{0,20}=\s{0,20}(?:\/(?![/*])|new\s{1,20}RegExp\b)/g;
|
|
16577
|
+
let m;
|
|
16578
|
+
while ((m = decl.exec(code)) !== null)
|
|
16579
|
+
names.add(m[1]);
|
|
16580
|
+
return names;
|
|
16581
|
+
}
|
|
15849
16582
|
function afterFirstArg(args) {
|
|
15850
16583
|
let depth = 0;
|
|
15851
16584
|
let quote = null;
|
|
@@ -15883,6 +16616,7 @@ function analyzeRegexMatchers(rawCode) {
|
|
|
15883
16616
|
let examined = 0;
|
|
15884
16617
|
let line = 1;
|
|
15885
16618
|
let lineAt2 = 0;
|
|
16619
|
+
let regexNames;
|
|
15886
16620
|
while ((m = re.exec(code)) !== null && examined++ < MAX_CALLS && out.length < MAX_REGEX_MATCHER_FINDINGS) {
|
|
15887
16621
|
const subj = readBalanced(code, m.index + m[0].length);
|
|
15888
16622
|
if (!subj)
|
|
@@ -15894,10 +16628,18 @@ function analyzeRegexMatchers(rawCode) {
|
|
|
15894
16628
|
if (!args)
|
|
15895
16629
|
continue;
|
|
15896
16630
|
const expected = mm[2] === "toHaveJSProperty" ? afterFirstArg(args.text) : args.text;
|
|
15897
|
-
if (!
|
|
15898
|
-
continue;
|
|
15899
|
-
if (mm[2] !== "toHaveJSProperty" && mm[2] !== "toContain" && (startsWithRegexLiteral(subj.text) || /^new\s+RegExp\b/.test(subj.text.trim())))
|
|
16631
|
+
if (!isBareRegexLiteral(expected))
|
|
15900
16632
|
continue;
|
|
16633
|
+
if (mm[2] !== "toHaveJSProperty" && mm[2] !== "toContain") {
|
|
16634
|
+
const subject = subj.text.trim();
|
|
16635
|
+
if (startsWithRegexLiteral(subject) || /^new\s+RegExp\b/.test(subject))
|
|
16636
|
+
continue;
|
|
16637
|
+
if (/^[A-Za-z_$][\w$]{0,80}$/.test(subject)) {
|
|
16638
|
+
regexNames ??= regexDeclaredNames(code);
|
|
16639
|
+
if (regexNames.has(subject))
|
|
16640
|
+
continue;
|
|
16641
|
+
}
|
|
16642
|
+
}
|
|
15901
16643
|
for (let i = code.indexOf("\n", lineAt2); i !== -1 && i < m.index; i = code.indexOf("\n", i + 1)) {
|
|
15902
16644
|
line++;
|
|
15903
16645
|
lineAt2 = i + 1;
|
|
@@ -15907,9 +16649,12 @@ function analyzeRegexMatchers(rawCode) {
|
|
|
15907
16649
|
return out;
|
|
15908
16650
|
}
|
|
15909
16651
|
function formatRegexMatcherNote(findings) {
|
|
15910
|
-
const lines = findings.map((f) =>
|
|
16652
|
+
const lines = findings.map((f) => {
|
|
16653
|
+
const negated = f.matcher === "toContain" ? "throws on a string and otherwise always passes (a regex never equals an item), so it checks nothing" : "always passes (a regex never equals the value), so it checks nothing";
|
|
16654
|
+
return `Line ${f.line}: .${f.negated ? "not." : ""}${f.matcher}(\u2026 /regex/ \u2026) ${f.negated ? negated : "can never pass: this matcher compares by equality and a regex never equals a value"}.`;
|
|
16655
|
+
});
|
|
15911
16656
|
return `${lines.join("\n")}
|
|
15912
|
-
A regex only works in toMatch, toHaveText, toContainText, toHaveValue and similar text matchers. For
|
|
16657
|
+
A regex only works in toMatch, toHaveText, toContainText, toHaveValue and similar text matchers. For the \`value\` of an input, textarea or select, use await expect(loc).toHaveValue(/re/). For any other DOM property (or element), poll it so the check still retries: await expect.poll(() => loc.evaluate((n) => n.<prop>)).toMatch(/re/). Never read it once with expect(await loc.evaluate(\u2026)), which sees the value before the page updates it. Or give toHaveJSProperty the exact string (or boolean) the page shows. Keep every other line as-is.`;
|
|
15913
16658
|
}
|
|
15914
16659
|
function lintTestQuality(rawCode, opts = {}) {
|
|
15915
16660
|
if (!rawCode || typeof rawCode !== "string")
|
|
@@ -16658,19 +17403,6 @@ var init_fence_runs = __esm({
|
|
|
16658
17403
|
}
|
|
16659
17404
|
});
|
|
16660
17405
|
|
|
16661
|
-
// ../shared/dist/trailing-slashes.js
|
|
16662
|
-
function stripTrailingSlashes(value) {
|
|
16663
|
-
let end = value.length;
|
|
16664
|
-
while (end > 0 && value.charCodeAt(end - 1) === 47)
|
|
16665
|
-
end--;
|
|
16666
|
-
return end === value.length ? value : value.slice(0, end);
|
|
16667
|
-
}
|
|
16668
|
-
var init_trailing_slashes = __esm({
|
|
16669
|
-
"../shared/dist/trailing-slashes.js"() {
|
|
16670
|
-
"use strict";
|
|
16671
|
-
}
|
|
16672
|
-
});
|
|
16673
|
-
|
|
16674
17406
|
// ../shared/dist/survey-bug-signature.js
|
|
16675
17407
|
function normalizeWords(text) {
|
|
16676
17408
|
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();
|
|
@@ -16694,14 +17426,17 @@ function requestSignatures(text) {
|
|
|
16694
17426
|
const statuses = [...text.matchAll(HTTP_STATUS)].map((m) => ({ status: m[1], at: m.index ?? 0, end: (m.index ?? 0) + m[0].length }));
|
|
16695
17427
|
if (statuses.length === 0)
|
|
16696
17428
|
return [];
|
|
16697
|
-
const paths = [...text.matchAll(PATH_IN_TEXT)].map((m) =>
|
|
16698
|
-
|
|
16699
|
-
|
|
16700
|
-
|
|
16701
|
-
|
|
16702
|
-
|
|
16703
|
-
|
|
16704
|
-
|
|
17429
|
+
const paths = [...text.matchAll(PATH_IN_TEXT)].map((m) => {
|
|
17430
|
+
const rawPath = stripTrailingChars(m[2] ?? m[3] ?? "", PATH_TRAILING_PUNCTUATION);
|
|
17431
|
+
return {
|
|
17432
|
+
method: m[1] ?? null,
|
|
17433
|
+
path: normalizeBugPath(rawPath),
|
|
17434
|
+
// Before normalisation, which turns a segment with a digit (favicon-32x32.png) into :id.
|
|
17435
|
+
incidental: isIncidentalAssetPath(rawPath.split(/[?#]/, 1)[0].toLowerCase()),
|
|
17436
|
+
at: m.index ?? 0,
|
|
17437
|
+
end: (m.index ?? 0) + m[0].length
|
|
17438
|
+
};
|
|
17439
|
+
}).filter((p) => p.path.length > 1);
|
|
16705
17440
|
const out = /* @__PURE__ */ new Map();
|
|
16706
17441
|
for (const { status, at, end } of statuses) {
|
|
16707
17442
|
const lead = STATUS_TO_PATH.exec(text.slice(end, end + 40));
|
|
@@ -16811,11 +17546,12 @@ function dedupeSiteWideSuspectedBugs(drafts) {
|
|
|
16811
17546
|
delete plans.suspectedBugs;
|
|
16812
17547
|
}
|
|
16813
17548
|
}
|
|
16814
|
-
var SIGNATURE_SOURCE_MAX_CHARS, UUID, PREFIXED_ID, HEX_RUN, NUMBER_RUN, GENERIC_JS_MESSAGES, GENERIC_JS_PREFIXES, JS_ERROR, MESSAGE_CLAUSE_END, HTTP_STATUS, PATH_IN_TEXT, STATUS_TO_PATH, INCIDENTAL_ASSET;
|
|
17549
|
+
var PATH_TRAILING_PUNCTUATION, SIGNATURE_SOURCE_MAX_CHARS, UUID, PREFIXED_ID, HEX_RUN, NUMBER_RUN, GENERIC_JS_MESSAGES, GENERIC_JS_PREFIXES, JS_ERROR, MESSAGE_CLAUSE_END, HTTP_STATUS, PATH_IN_TEXT, STATUS_TO_PATH, INCIDENTAL_ASSET;
|
|
16815
17550
|
var init_survey_bug_signature = __esm({
|
|
16816
17551
|
"../shared/dist/survey-bug-signature.js"() {
|
|
16817
17552
|
"use strict";
|
|
16818
17553
|
init_trailing_slashes();
|
|
17554
|
+
PATH_TRAILING_PUNCTUATION = /[.,;:]/;
|
|
16819
17555
|
SIGNATURE_SOURCE_MAX_CHARS = 800;
|
|
16820
17556
|
UUID = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/g;
|
|
16821
17557
|
PREFIXED_ID = /\b[a-z]{1,8}[-_]\d+\b/g;
|
|
@@ -18146,7 +18882,7 @@ function pathIdsIn(text) {
|
|
|
18146
18882
|
function findCounterIdPins(code) {
|
|
18147
18883
|
if (code.length > CLOCK_MAX_CODE_CHARS)
|
|
18148
18884
|
return [];
|
|
18149
|
-
const
|
|
18885
|
+
const clean3 = stripLineComments(blankBlockComments3(code));
|
|
18150
18886
|
const fillValues = [];
|
|
18151
18887
|
const candidates = [];
|
|
18152
18888
|
let seenWrite = false;
|
|
@@ -18154,24 +18890,24 @@ function findCounterIdPins(code) {
|
|
|
18154
18890
|
CALL_RE.lastIndex = 0;
|
|
18155
18891
|
let m;
|
|
18156
18892
|
let calls = 0;
|
|
18157
|
-
while (calls++ < MAX_CALLS2 && (m = CALL_RE.exec(
|
|
18893
|
+
while (calls++ < MAX_CALLS2 && (m = CALL_RE.exec(clean3)) !== null) {
|
|
18158
18894
|
const name = m[1];
|
|
18159
18895
|
const open = m.index + m[0].length - 1;
|
|
18160
18896
|
if (VALUE_WRITES.has(name)) {
|
|
18161
18897
|
seenWrite = true;
|
|
18162
|
-
const args2 = argsAt(
|
|
18898
|
+
const args2 = argsAt(clean3, open);
|
|
18163
18899
|
if (args2 !== null)
|
|
18164
18900
|
fillValues.push(...staticStrings(args2));
|
|
18165
18901
|
continue;
|
|
18166
18902
|
}
|
|
18167
18903
|
if (createIndex === -1) {
|
|
18168
|
-
if (name === "click" && seenWrite && isCreateClick(
|
|
18904
|
+
if (name === "click" && seenWrite && isCreateClick(clean3, m.index))
|
|
18169
18905
|
createIndex = m.index;
|
|
18170
18906
|
continue;
|
|
18171
18907
|
}
|
|
18172
18908
|
if (name === "click")
|
|
18173
18909
|
continue;
|
|
18174
|
-
const args = argsAt(
|
|
18910
|
+
const args = argsAt(clean3, open);
|
|
18175
18911
|
if (args === null)
|
|
18176
18912
|
continue;
|
|
18177
18913
|
const isUrl = URL_SINKS.has(name);
|
|
@@ -18190,7 +18926,7 @@ function findCounterIdPins(code) {
|
|
|
18190
18926
|
}
|
|
18191
18927
|
if (createIndex === -1)
|
|
18192
18928
|
return [];
|
|
18193
|
-
const before =
|
|
18929
|
+
const before = clean3.slice(0, createIndex);
|
|
18194
18930
|
let pathIdsBefore = null;
|
|
18195
18931
|
const findings = [];
|
|
18196
18932
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -18214,7 +18950,7 @@ function findCounterIdPins(code) {
|
|
|
18214
18950
|
continue;
|
|
18215
18951
|
}
|
|
18216
18952
|
}
|
|
18217
|
-
findings.push({ kind: c.kind, literal: c.literal, line: lineAt(
|
|
18953
|
+
findings.push({ kind: c.kind, literal: c.literal, line: lineAt(clean3, c.index) });
|
|
18218
18954
|
}
|
|
18219
18955
|
return findings;
|
|
18220
18956
|
}
|
|
@@ -18509,7 +19245,7 @@ var init_suspicious_expectation = __esm({
|
|
|
18509
19245
|
NOT_STUCK_RE = /\b(?:not|never|no longer|isn't|doesn't|don't|without)\s+(?:\w+\s+){0,2}?(?:stuck|hang|hangs|hung|freeze|freezes|frozen|spin|spins|spinning)\b/i;
|
|
18510
19246
|
LISTS_STUCK_RE = /\b(?:appears? in (?:the )?[\w ]{0,25}(?:report|list|dashboard|queue)|(?:lists?|monitors?|shows?|reports?) (?:\w+ ){0,2}(?:that|which|whose) (?:are |is )?(?:stuck|hangs?|hung|frozen)|can (?:retry|re-?run) (?:\w+ ){0,2}(?:that (?:are )?)?stuck)\b/i;
|
|
18511
19247
|
STUCK_VETO_RE = new RegExp(`${NOT_STUCK_RE.source}|${LISTS_STUCK_RE.source}`, "i");
|
|
18512
|
-
NOT_SILENT_RE = /\b(?:not|never|\w+n't|instead of|rather than|avoids?|prevents?)\s+(?:\w+\s+){0,1}?(?:fail(?:s|ing|ed)?|ignor\w*)
|
|
19248
|
+
NOT_SILENT_RE = /\b(?:not|never|\w+n't|instead of|rather than|avoids?|prevents?)\s+(?:\w+\s+){0,1}?(?:(?:fail(?:s|ing|ed)?|ignor\w*)\s*)?silent(?:ly)?\b/i;
|
|
18513
19249
|
PENDING_WORD = "(?:sending|loading|saving|searching|checking(?!\\s+(?:account|balance|out|in)\\b)|syncing|refreshing|fetching|validating|processing|submitting|uploading|posting|publishing|creating|updating|deleting|connecting|verifying|signing in|logging in)";
|
|
18514
19250
|
ELLIPSIS = "(?:\\.{3}|\u2026)";
|
|
18515
19251
|
NOT_STAY_RE = /\b(?:not|never|no longer|without|n't|cannot)\s+(?:\w+\s+){0,2}?(?:stay|stays|stayed|remain|remains|remained|leave|leaves|left)\b/i;
|
|
@@ -18665,7 +19401,7 @@ function outcomeNamedByAssertion(outcome, code) {
|
|
|
18665
19401
|
function namedByStatements(outcome, statements) {
|
|
18666
19402
|
const text = outcomeText(outcome);
|
|
18667
19403
|
if (outcome.type === "url") {
|
|
18668
|
-
const routes = (text.match(ROUTE_TERM) ?? []).map((r) => r
|
|
19404
|
+
const routes = (text.match(ROUTE_TERM) ?? []).map((r) => stripTrailingChars(r, ROUTE_TRAILING_PUNCTUATION)).filter((r) => r.length > 1);
|
|
18669
19405
|
return statements.some((stmt) => {
|
|
18670
19406
|
if (!/\.(?:waitForURL|toHaveURL)\s{0,20}\(/.test(stmt))
|
|
18671
19407
|
return false;
|
|
@@ -18837,7 +19573,7 @@ function evaluatePromotionGate(input, checks = PROMOTION_QUALITY_CHECKS) {
|
|
|
18837
19573
|
...suspiciousTagged ? { suspiciousPass: { mode: suspicious, covered: suspiciousCovered, wouldHold: suspiciousHold } } : {}
|
|
18838
19574
|
};
|
|
18839
19575
|
}
|
|
18840
|
-
var SOURCE_RANK, strengthCheck, STRING_CONST, TYPED_VALUE, TYPED_CONSTANT, MAX_STATEMENTS, MAX_STATEMENT_CHARS, LITERAL_OR_IDENTIFIER, MUTATING_ACTION, FEEDBACK_REGION, ONE_SHOT_MESSAGE_REASON, FEEDBACK_REGION_ALL, dynamicAssertionCheck, OUTCOME_KIND_MATCHER, ROUTE_TERM, QUOTED_LITERAL, GENERIC_LITERAL, MAX_OUTCOME_STATEMENTS, MAX_OUTCOMES, MAX_OUTCOME_TEXT_CHARS, expectedOutcomeCheck, testDataCheck, sharedAccountCheck, clockLiteralCheck, readinessPollCheck, knownBugTestCheck, bugClaimContractCheck, suspiciousExpectationCheck, bugCandidateCheck, conventionOutcomesCheck, pendingHook, locatorFragmentCheck, FILTER_STEP, NEXT_ACTION, REDUCTION_PROOF, MAX_FILTER_LINES, MAX_FILTER_LINE_CHARS, filterPositiveOnlyCheck, PROMOTION_QUALITY_CHECKS, PROMOTION_GATE_MAX_CODE_CHARS, NOT_EVALUATED, SUSPICIOUS_PASS_TAG, NOT_CONTENT_MATCHER, CONTENT_PROOF, NAMED_ELEMENT, NEEDS_REVIEW_TAG, count2, RECHECKED_NOTES, EVIDENCE_NOTE_PREFIXES, PENDING_RECHECK_NOTE_PREFIXES, STRENGTH_RANK;
|
|
19576
|
+
var SOURCE_RANK, strengthCheck, STRING_CONST, TYPED_VALUE, TYPED_CONSTANT, MAX_STATEMENTS, MAX_STATEMENT_CHARS, LITERAL_OR_IDENTIFIER, MUTATING_ACTION, FEEDBACK_REGION, ONE_SHOT_MESSAGE_REASON, FEEDBACK_REGION_ALL, dynamicAssertionCheck, OUTCOME_KIND_MATCHER, ROUTE_TERM, ROUTE_TRAILING_PUNCTUATION, QUOTED_LITERAL, GENERIC_LITERAL, MAX_OUTCOME_STATEMENTS, MAX_OUTCOMES, MAX_OUTCOME_TEXT_CHARS, expectedOutcomeCheck, testDataCheck, sharedAccountCheck, clockLiteralCheck, readinessPollCheck, knownBugTestCheck, bugClaimContractCheck, suspiciousExpectationCheck, bugCandidateCheck, conventionOutcomesCheck, pendingHook, locatorFragmentCheck, FILTER_STEP, NEXT_ACTION, REDUCTION_PROOF, MAX_FILTER_LINES, MAX_FILTER_LINE_CHARS, filterPositiveOnlyCheck, PROMOTION_QUALITY_CHECKS, PROMOTION_GATE_MAX_CODE_CHARS, NOT_EVALUATED, SUSPICIOUS_PASS_TAG, NOT_CONTENT_MATCHER, CONTENT_PROOF, NAMED_ELEMENT, NEEDS_REVIEW_TAG, count2, RECHECKED_NOTES, EVIDENCE_NOTE_PREFIXES, PENDING_RECHECK_NOTE_PREFIXES, STRENGTH_RANK;
|
|
18841
19577
|
var init_promotion_quality_gate = __esm({
|
|
18842
19578
|
"../shared/dist/promotion-quality-gate.js"() {
|
|
18843
19579
|
"use strict";
|
|
@@ -18852,11 +19588,12 @@ var init_promotion_quality_gate = __esm({
|
|
|
18852
19588
|
init_run_classification();
|
|
18853
19589
|
init_expected_outcome_coverage();
|
|
18854
19590
|
init_expect_call();
|
|
19591
|
+
init_trailing_slashes();
|
|
18855
19592
|
SOURCE_RANK = { generator: 0, classifier: 0, healer: 1, human: 2 };
|
|
18856
19593
|
strengthCheck = {
|
|
18857
19594
|
id: "assertion-strength",
|
|
18858
|
-
run({ code, tags, strengthGate, flagContradictedAdditions }) {
|
|
18859
|
-
const verdict = validateAssertionStrength(code, { flagContradictedAdditions: flagContradictedAdditions === true });
|
|
19595
|
+
run({ code, tags, strengthGate, flagContradictedAdditions, lastActionProof }) {
|
|
19596
|
+
const verdict = validateAssertionStrength(code, { flagContradictedAdditions: flagContradictedAdditions === true, lastActionProof });
|
|
18860
19597
|
if (verdict.severity === "route-bug")
|
|
18861
19598
|
return [{ check: "route-bug" }];
|
|
18862
19599
|
const authDowngrade = verdict.severity === "downgrade" && tags.some((t) => t.toLowerCase() === "@auth");
|
|
@@ -18874,7 +19611,7 @@ var init_promotion_quality_gate = __esm({
|
|
|
18874
19611
|
MAX_STATEMENTS = 500;
|
|
18875
19612
|
MAX_STATEMENT_CHARS = 400;
|
|
18876
19613
|
LITERAL_OR_IDENTIFIER = /'(?:[^'\\\n]|\\.){0,500}'|"(?:[^"\\\n]|\\.){0,500}"|`(?:[^`\\]|\\.){0,2000}`|(?<![\w$.])[A-Za-z_$][\w$]{0,60}/g;
|
|
18877
|
-
MUTATING_ACTION = /\.(?:click|dblclick|tap|fill|type|pressSequentially|press|check|uncheck|selectOption|setInputFiles|dragTo|clear)\s{0,20}\(|\brequest\s{0,20}\.\s{0,20}(?:post|put|patch|delete|fetch)\s{0,20}\(/;
|
|
19614
|
+
MUTATING_ACTION = /\.(?:click|dblclick|tap|fill|type|pressSequentially|press|check|uncheck|setChecked|selectOption|setInputFiles|dragTo|dragAndDrop|clear)\s{0,20}\(|\brequest\s{0,20}\.\s{0,20}(?:post|put|patch|delete|fetch)\s{0,20}\(/;
|
|
18878
19615
|
FEEDBACK_REGION = /getByRole\s{0,20}\(\s{0,20}['"`](?:alert|status)['"`]|\baria-live\b|\btoast|\bsnackbar\b|\bflash.?message\b/i;
|
|
18879
19616
|
ONE_SHOT_MESSAGE_REASON = "one-shot success/error banner text";
|
|
18880
19617
|
FEEDBACK_REGION_ALL = /getByRole\s{0,20}\(\s{0,20}['"`](?:alert|status)['"`]|\baria-live\b|\btoast|\bsnackbar\b|\bflash.?message\b/gi;
|
|
@@ -18933,6 +19670,7 @@ var init_promotion_quality_gate = __esm({
|
|
|
18933
19670
|
"element-hidden": /\.(?:toBeHidden|not\s{0,20}\.\s{0,20}toBeVisible|not\s{0,20}\.\s{0,20}toBeAttached|not\s{0,20}\.\s{0,20}toContainText)\s{0,20}\(|\.toHaveCount\s{0,20}\(\s{0,20}0\s{0,20}\)/
|
|
18934
19671
|
};
|
|
18935
19672
|
ROUTE_TERM = /\/[A-Za-z0-9_./?&=#-]{1,200}/g;
|
|
19673
|
+
ROUTE_TRAILING_PUNCTUATION = /[.,;:!?)]/;
|
|
18936
19674
|
QUOTED_LITERAL = /'([^'\\\n]{3,200})'|"([^"\\\n]{3,200})"|`([^`\\$\n]{3,200})`/g;
|
|
18937
19675
|
GENERIC_LITERAL = /^(?:true|false|button|link|heading|dialog|alertdialog|article|main|region|status|alert|textbox|combobox|checkbox|radio|tab|tabpanel|option|list|listitem|row|cell|img|navigation|banner|complementary|contentinfo|form|search|menu|menuitem|visible|hidden|attached|detached|domcontentloaded|load|networkidle)$/i;
|
|
18938
19676
|
MAX_OUTCOME_STATEMENTS = 300;
|
|
@@ -19052,8 +19790,8 @@ ${goalText}`);
|
|
|
19052
19790
|
return count3 > 0 ? [{ check: "locator-fragment", detail: String(count3), soft: true }] : [];
|
|
19053
19791
|
}
|
|
19054
19792
|
};
|
|
19055
|
-
FILTER_STEP = /\.(?:getByLabel|getByPlaceholder|getByRole|getByTitle)\s*\([^)\n]{0,200}?\b(?:filter|sort|search|status|delete|remove|dismiss|archive|clear)\b[^)\n]{0,200}\)[^\n]{0,200}\.(?:selectOption|fill|type|pressSequentially|click|check|uncheck|press)\s*\(/i;
|
|
19056
|
-
NEXT_ACTION = /\.(?:goto|reload|click|dblclick|tap|fill|type|pressSequentially|press|check|uncheck|selectOption|setInputFiles|dragTo|clear)\s*\(/;
|
|
19793
|
+
FILTER_STEP = /\.(?:getByLabel|getByPlaceholder|getByRole|getByTitle)\s*\([^)\n]{0,200}?\b(?:filter|sort|search|status|delete|remove|dismiss|archive|clear)\b[^)\n]{0,200}\)[^\n]{0,200}\.(?:selectOption|fill|type|pressSequentially|click|check|uncheck|setChecked|press)\s*\(/i;
|
|
19794
|
+
NEXT_ACTION = /\.(?:goto|reload|click|dblclick|tap|fill|type|pressSequentially|press|check|uncheck|setChecked|selectOption|setInputFiles|dragTo|dragAndDrop|clear)\s*\(/;
|
|
19057
19795
|
REDUCTION_PROOF = /\.toHaveCount\s*\(|\.not\s*\.\s*(?:toBeVisible|toBeAttached|toContainText|toHaveText)\s*\(|\.toBeHidden\s*\(|\bShowing\b|\bof \d+\b|\b\d+ (?:results?|rows?|items?|candidates?|entries|matches|records)\b|\bNo (?:[\w'-]+ ){0,3}(?:match|matches|results?|found|left|remain|yet)\b|\bNothing (?:to show|found|matches)\b|\bis empty\b|\btoHaveURL\b/i;
|
|
19058
19796
|
MAX_FILTER_LINES = 2e3;
|
|
19059
19797
|
MAX_FILTER_LINE_CHARS = 600;
|
|
@@ -19168,7 +19906,7 @@ ${goalText}`);
|
|
|
19168
19906
|
});
|
|
19169
19907
|
|
|
19170
19908
|
// ../shared/dist/regex-alternation.js
|
|
19171
|
-
function
|
|
19909
|
+
function closingParens2(source) {
|
|
19172
19910
|
const close = new Int32Array(source.length).fill(-1);
|
|
19173
19911
|
const open = [];
|
|
19174
19912
|
let inClass = false;
|
|
@@ -19193,7 +19931,7 @@ function closingParens(source) {
|
|
|
19193
19931
|
return close;
|
|
19194
19932
|
}
|
|
19195
19933
|
function alternatesAtTop(source) {
|
|
19196
|
-
const close =
|
|
19934
|
+
const close = closingParens2(source);
|
|
19197
19935
|
let start = source.startsWith("^") ? 1 : 0;
|
|
19198
19936
|
let end = source.endsWith("$") ? source.length - 1 : source.length;
|
|
19199
19937
|
while (start < end && source[start] === "(" && close[start] === end - 1) {
|
|
@@ -19242,6 +19980,124 @@ var init_page_evidence = __esm({
|
|
|
19242
19980
|
}
|
|
19243
19981
|
});
|
|
19244
19982
|
|
|
19983
|
+
// ../shared/dist/stated-limits.js
|
|
19984
|
+
function validDate(v) {
|
|
19985
|
+
const [y, m, d] = v.split("-").map(Number);
|
|
19986
|
+
const t = new Date(Date.UTC(y, m - 1, d));
|
|
19987
|
+
return t.getUTCFullYear() === y && t.getUTCMonth() === m - 1 && t.getUTCDate() === d;
|
|
19988
|
+
}
|
|
19989
|
+
function isoFor(type, v) {
|
|
19990
|
+
const re = DATE_RE[type];
|
|
19991
|
+
if (!re || !re.test(v))
|
|
19992
|
+
return false;
|
|
19993
|
+
if (type === "date")
|
|
19994
|
+
return validDate(v);
|
|
19995
|
+
if (type === "datetime-local")
|
|
19996
|
+
return validDate(v.slice(0, 10)) && Number(v.slice(11, 13)) < 24 && Number(v.slice(14, 16)) < 60;
|
|
19997
|
+
if (type === "time")
|
|
19998
|
+
return Number(v.slice(0, 2)) < 24 && Number(v.slice(3, 5)) < 60;
|
|
19999
|
+
if (type === "month")
|
|
20000
|
+
return Number(v.slice(5, 7)) >= 1 && Number(v.slice(5, 7)) <= 12;
|
|
20001
|
+
return Number(v.slice(6, 8)) >= 1 && Number(v.slice(6, 8)) <= 53;
|
|
20002
|
+
}
|
|
20003
|
+
function clean2(raw) {
|
|
20004
|
+
return typeof raw === "string" ? raw.trim() : "";
|
|
20005
|
+
}
|
|
20006
|
+
function statedLimitsFromAttrs(attrs, inputType) {
|
|
20007
|
+
const type = (inputType ?? "").trim().toLowerCase();
|
|
20008
|
+
const limits = [];
|
|
20009
|
+
const add = (k, v) => {
|
|
20010
|
+
limits.push({ k, v });
|
|
20011
|
+
};
|
|
20012
|
+
if (NUMBER_TYPES.has(type)) {
|
|
20013
|
+
for (const [attr, k] of [["min", "min"], ["max", "max"]]) {
|
|
20014
|
+
const v = clean2(attrs[attr]);
|
|
20015
|
+
if (NUMBER_RE.test(v))
|
|
20016
|
+
add(k, v);
|
|
20017
|
+
}
|
|
20018
|
+
const step = clean2(attrs.step);
|
|
20019
|
+
if (NUMBER_RE.test(step) && Number(step) > 0)
|
|
20020
|
+
add("step", step);
|
|
20021
|
+
} else if (type in DATE_RE) {
|
|
20022
|
+
for (const [attr, k] of [["min", "min"], ["max", "max"]]) {
|
|
20023
|
+
const v = clean2(attrs[attr]);
|
|
20024
|
+
if (isoFor(type, v))
|
|
20025
|
+
add(k, v);
|
|
20026
|
+
}
|
|
20027
|
+
} else if (TEXT_TYPES.has(type)) {
|
|
20028
|
+
for (const [attr, k] of [["minlength", "minLength"], ["maxlength", "maxLength"]]) {
|
|
20029
|
+
const v = clean2(attrs[attr]);
|
|
20030
|
+
if (LENGTH_RE.test(v))
|
|
20031
|
+
add(k, String(Number(v)));
|
|
20032
|
+
}
|
|
20033
|
+
const pattern = cleanPattern(clean2(attrs.pattern).slice(0, STATED_PATTERN_MAX));
|
|
20034
|
+
if (pattern)
|
|
20035
|
+
add("pattern", pattern);
|
|
20036
|
+
}
|
|
20037
|
+
return limits.length > 0 ? { v: STATED_LIMITS_VERSION, limits } : null;
|
|
20038
|
+
}
|
|
20039
|
+
function cleanPattern(text) {
|
|
20040
|
+
return text.replace(/[\u0000-\u001f\u007f]/g, "").replace(/[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/g, "");
|
|
20041
|
+
}
|
|
20042
|
+
function readStatedLimits(stored) {
|
|
20043
|
+
if (!stored || typeof stored !== "object" || Array.isArray(stored))
|
|
20044
|
+
return [];
|
|
20045
|
+
const rec = stored;
|
|
20046
|
+
if (rec.v !== STATED_LIMITS_VERSION || !Array.isArray(rec.limits))
|
|
20047
|
+
return [];
|
|
20048
|
+
const out = [];
|
|
20049
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20050
|
+
for (const item of rec.limits.slice(0, MAX_LIMITS * 2)) {
|
|
20051
|
+
if (!item || typeof item !== "object")
|
|
20052
|
+
continue;
|
|
20053
|
+
const { k, v } = item;
|
|
20054
|
+
if (typeof k !== "string" || typeof v !== "string" || !KINDS.includes(k) || seen.has(k))
|
|
20055
|
+
continue;
|
|
20056
|
+
if (v.length === 0 || v.length > STATED_PATTERN_MAX)
|
|
20057
|
+
continue;
|
|
20058
|
+
const text = k === "pattern" ? cleanPattern(v) : v;
|
|
20059
|
+
if (text === "")
|
|
20060
|
+
continue;
|
|
20061
|
+
if (k === "minLength" || k === "maxLength") {
|
|
20062
|
+
if (!LENGTH_RE.test(v))
|
|
20063
|
+
continue;
|
|
20064
|
+
} else if (k === "step") {
|
|
20065
|
+
if (!NUMBER_RE.test(v))
|
|
20066
|
+
continue;
|
|
20067
|
+
} else if (k === "min" || k === "max") {
|
|
20068
|
+
if (!NUMBER_RE.test(v) && !Object.values(DATE_RE).some((re) => re.test(v)))
|
|
20069
|
+
continue;
|
|
20070
|
+
}
|
|
20071
|
+
seen.add(k);
|
|
20072
|
+
out.push({ k, v: text });
|
|
20073
|
+
}
|
|
20074
|
+
return out;
|
|
20075
|
+
}
|
|
20076
|
+
function statedLimitsRecord(limits) {
|
|
20077
|
+
return limits.length > 0 ? { v: STATED_LIMITS_VERSION, limits } : null;
|
|
20078
|
+
}
|
|
20079
|
+
var STATED_LIMITS_VERSION, STATED_PATTERN_MAX, KINDS, MAX_LIMITS, TEXT_TYPES, NUMBER_TYPES, DATE_RE, NUMBER_RE, LENGTH_RE;
|
|
20080
|
+
var init_stated_limits = __esm({
|
|
20081
|
+
"../shared/dist/stated-limits.js"() {
|
|
20082
|
+
"use strict";
|
|
20083
|
+
STATED_LIMITS_VERSION = 1;
|
|
20084
|
+
STATED_PATTERN_MAX = 200;
|
|
20085
|
+
KINDS = ["min", "max", "minLength", "maxLength", "step", "pattern"];
|
|
20086
|
+
MAX_LIMITS = KINDS.length;
|
|
20087
|
+
TEXT_TYPES = /* @__PURE__ */ new Set(["text", "search", "email", "url", "tel", "password", "textarea", ""]);
|
|
20088
|
+
NUMBER_TYPES = /* @__PURE__ */ new Set(["number", "range"]);
|
|
20089
|
+
DATE_RE = {
|
|
20090
|
+
date: /^\d{4}-\d{2}-\d{2}$/,
|
|
20091
|
+
"datetime-local": /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?$/,
|
|
20092
|
+
time: /^\d{2}:\d{2}(?::\d{2})?$/,
|
|
20093
|
+
month: /^\d{4}-\d{2}$/,
|
|
20094
|
+
week: /^\d{4}-W\d{2}$/
|
|
20095
|
+
};
|
|
20096
|
+
NUMBER_RE = /^-?\d{1,15}(?:\.\d{1,10})?$/;
|
|
20097
|
+
LENGTH_RE = /^\d{1,7}$/;
|
|
20098
|
+
}
|
|
20099
|
+
});
|
|
20100
|
+
|
|
19245
20101
|
// ../shared/dist/index.js
|
|
19246
20102
|
var dist_exports = {};
|
|
19247
20103
|
__export(dist_exports, {
|
|
@@ -19249,9 +20105,11 @@ __export(dist_exports, {
|
|
|
19249
20105
|
ACTION_BATCH_MAX_SIZE: () => ACTION_BATCH_MAX_SIZE,
|
|
19250
20106
|
ADDON_DEFINITIONS: () => ADDON_DEFINITIONS,
|
|
19251
20107
|
ADMIN_EMAIL_LIMITS: () => ADMIN_EMAIL_LIMITS,
|
|
20108
|
+
ADMIN_FORCE_CANCELLED_RUN_ERROR: () => ADMIN_FORCE_CANCELLED_RUN_ERROR,
|
|
19252
20109
|
ADMIN_VIEW_DEFAULT_RETURN_TO: () => ADMIN_VIEW_DEFAULT_RETURN_TO,
|
|
19253
20110
|
AGENT_AI_PROVIDER_PREFIXES: () => AGENT_AI_PROVIDER_PREFIXES,
|
|
19254
20111
|
AI_CREDIT_MARGIN: () => AI_CREDIT_MARGIN,
|
|
20112
|
+
ALLOWED_PROVIDER_CHAIN_HOPS: () => ALLOWED_PROVIDER_CHAIN_HOPS,
|
|
19255
20113
|
API_AUTH_HEADERS_ENV_KEY: () => API_AUTH_HEADERS_ENV_KEY,
|
|
19256
20114
|
API_MODEL_CATALOG: () => API_MODEL_CATALOG,
|
|
19257
20115
|
API_MODEL_FIELDS: () => API_MODEL_FIELDS,
|
|
@@ -19291,12 +20149,14 @@ __export(dist_exports, {
|
|
|
19291
20149
|
BUG_TEST_BROKEN_REASON: () => BUG_TEST_BROKEN_REASON,
|
|
19292
20150
|
BUG_TEST_BROKEN_TAG: () => BUG_TEST_BROKEN_TAG,
|
|
19293
20151
|
CACHE_PRICE_PER_M: () => CACHE_PRICE_PER_M,
|
|
20152
|
+
CANCELLED_REASON_PREFIXES: () => CANCELLED_REASON_PREFIXES,
|
|
19294
20153
|
CANVAS_UNSUPPORTED_BODY: () => CANVAS_UNSUPPORTED_BODY,
|
|
19295
20154
|
CANVAS_UNSUPPORTED_CODE: () => CANVAS_UNSUPPORTED_CODE,
|
|
19296
20155
|
CANVAS_UNSUPPORTED_NOT_CHARGED: () => CANVAS_UNSUPPORTED_NOT_CHARGED,
|
|
19297
20156
|
CANVAS_UNSUPPORTED_OVERRIDE_HINT: () => CANVAS_UNSUPPORTED_OVERRIDE_HINT,
|
|
19298
20157
|
CANVAS_UNSUPPORTED_TITLE: () => CANVAS_UNSUPPORTED_TITLE,
|
|
19299
20158
|
CAPTURE_JSON_STRUCTURE_LIMITS: () => CAPTURE_JSON_STRUCTURE_LIMITS,
|
|
20159
|
+
CHAIN_CANCELLED_RUN_ERROR: () => CHAIN_CANCELLED_RUN_ERROR,
|
|
19300
20160
|
CHAT_FAILOVER_KEY: () => CHAT_FAILOVER_KEY,
|
|
19301
20161
|
CLAUDE_CLI_DEFAULT_EFFORT: () => CLAUDE_CLI_DEFAULT_EFFORT,
|
|
19302
20162
|
CLAUDE_CLI_DEFAULT_MODEL: () => CLAUDE_CLI_DEFAULT_MODEL,
|
|
@@ -19333,6 +20193,7 @@ __export(dist_exports, {
|
|
|
19333
20193
|
DEFAULT_GEMINI_SCREEN_MODEL: () => DEFAULT_GEMINI_SCREEN_MODEL,
|
|
19334
20194
|
DEFAULT_JSON_STRUCTURE_LIMITS: () => DEFAULT_JSON_STRUCTURE_LIMITS,
|
|
19335
20195
|
DEFAULT_PLATFORM_DB_POOL_SIZE: () => DEFAULT_PLATFORM_DB_POOL_SIZE,
|
|
20196
|
+
DEFAULT_PROVIDER_ACCOUNT_ID: () => DEFAULT_PROVIDER_ACCOUNT_ID,
|
|
19336
20197
|
DEFAULT_SHARED_DB_POOL_SIZE: () => DEFAULT_SHARED_DB_POOL_SIZE,
|
|
19337
20198
|
DEFAULT_STT_MODEL: () => DEFAULT_STT_MODEL,
|
|
19338
20199
|
DEFAULT_STT_MODEL_BY_PROVIDER: () => DEFAULT_STT_MODEL_BY_PROVIDER,
|
|
@@ -19349,6 +20210,7 @@ __export(dist_exports, {
|
|
|
19349
20210
|
ENVIRONMENTAL_ERROR_STORED_PREFIXES: () => ENVIRONMENTAL_ERROR_STORED_PREFIXES,
|
|
19350
20211
|
EVIDENCE_NOTE_PREFIXES: () => EVIDENCE_NOTE_PREFIXES,
|
|
19351
20212
|
EXPECT_CALL_RE: () => EXPECT_CALL_RE,
|
|
20213
|
+
FEATURE_ROW_RUN_WINDOW: () => FEATURE_ROW_RUN_WINDOW,
|
|
19352
20214
|
FLOW_PHASE_LABELS: () => FLOW_PHASE_LABELS,
|
|
19353
20215
|
FLOW_PHASE_TYPES: () => FLOW_PHASE_TYPES,
|
|
19354
20216
|
FLOW_SERVER_TYPES: () => FLOW_SERVER_TYPES,
|
|
@@ -19416,6 +20278,8 @@ __export(dist_exports, {
|
|
|
19416
20278
|
MAX_ORG_NAME_LENGTH: () => MAX_ORG_NAME_LENGTH,
|
|
19417
20279
|
MAX_PASSWORD_BYTES: () => MAX_PASSWORD_BYTES,
|
|
19418
20280
|
MAX_PLAYWRIGHT_CODE_LENGTH: () => MAX_PLAYWRIGHT_CODE_LENGTH,
|
|
20281
|
+
MAX_PROVIDER_ACCOUNTS: () => MAX_PROVIDER_ACCOUNTS,
|
|
20282
|
+
MAX_PROVIDER_ACCOUNT_MODELS: () => MAX_PROVIDER_ACCOUNT_MODELS,
|
|
19419
20283
|
MAX_SURVEY_ENTRY_URLS: () => MAX_SURVEY_ENTRY_URLS,
|
|
19420
20284
|
MAX_SURVEY_ENTRY_URL_LENGTH: () => MAX_SURVEY_ENTRY_URL_LENGTH,
|
|
19421
20285
|
MCP_ARMY_LIMITS: () => MCP_ARMY_LIMITS,
|
|
@@ -19451,7 +20315,7 @@ __export(dist_exports, {
|
|
|
19451
20315
|
MUSE_ENGINE_DEFAULT_MODEL: () => MUSE_ENGINE_DEFAULT_MODEL,
|
|
19452
20316
|
MUSE_ENGINE_MODELS: () => MUSE_ENGINE_MODELS,
|
|
19453
20317
|
NOREPLY_EMAIL: () => NOREPLY_EMAIL,
|
|
19454
|
-
|
|
20318
|
+
OPENCODE_GO_DEEPSEEK_GLM_MODELS: () => OPENCODE_GO_DEEPSEEK_GLM_MODELS,
|
|
19455
20319
|
OPENCODE_GO_MODEL_PATTERN: () => OPENCODE_GO_MODEL_PATTERN,
|
|
19456
20320
|
OPENCODE_GO_PREFIX: () => OPENCODE_GO_PREFIX,
|
|
19457
20321
|
OPENROUTER_MAX_CALL_COST_USD: () => OPENROUTER_MAX_CALL_COST_USD,
|
|
@@ -19471,10 +20335,12 @@ __export(dist_exports, {
|
|
|
19471
20335
|
PLAN_FEATURES: () => PLAN_FEATURES,
|
|
19472
20336
|
PLAN_FEATURE_LOCKED_CODE: () => PLAN_FEATURE_LOCKED_CODE,
|
|
19473
20337
|
PLAN_ORDER: () => PLAN_ORDER,
|
|
20338
|
+
PLATFORM_ADMIN_CANCELLED_RUN_ERROR: () => PLATFORM_ADMIN_CANCELLED_RUN_ERROR,
|
|
19474
20339
|
PLATFORM_ADMIN_EMAIL: () => PLATFORM_ADMIN_EMAIL,
|
|
19475
20340
|
PLATFORM_DEFAULT_PROVIDER: () => PLATFORM_DEFAULT_PROVIDER,
|
|
19476
20341
|
PLATFORM_PLAN_BILLING: () => PLATFORM_PLAN_BILLING,
|
|
19477
20342
|
PLATFORM_SERVER_APPLICATION_NAME: () => PLATFORM_SERVER_APPLICATION_NAME,
|
|
20343
|
+
PLATFORM_SUPPORT_CANCELLED_RUN_ERROR: () => PLATFORM_SUPPORT_CANCELLED_RUN_ERROR,
|
|
19478
20344
|
POST_SPECIAL_TRIAL_DAYS: () => POST_SPECIAL_TRIAL_DAYS,
|
|
19479
20345
|
PRESET_DIRECT_FALLBACK: () => PRESET_DIRECT_FALLBACK,
|
|
19480
20346
|
PRODUCTION_READ_ONLY_GUARD_TEMPLATE: () => PRODUCTION_READ_ONLY_GUARD_TEMPLATE,
|
|
@@ -19486,8 +20352,12 @@ __export(dist_exports, {
|
|
|
19486
20352
|
PROJECT_OVERRIDE_FIELDS: () => PROJECT_OVERRIDE_FIELDS,
|
|
19487
20353
|
PROMOTION_GATE_MAX_CODE_CHARS: () => PROMOTION_GATE_MAX_CODE_CHARS,
|
|
19488
20354
|
PROMOTION_QUALITY_CHECKS: () => PROMOTION_QUALITY_CHECKS,
|
|
20355
|
+
PROVIDER_ACCOUNT_ID_PATTERN: () => PROVIDER_ACCOUNT_ID_PATTERN,
|
|
20356
|
+
PROVIDER_ACCOUNT_MODEL_PATTERN: () => PROVIDER_ACCOUNT_MODEL_PATTERN,
|
|
19489
20357
|
PROVIDER_CHAINS: () => PROVIDER_CHAINS,
|
|
20358
|
+
PROVIDER_CHAINS_SETTING_MAX_CHARS: () => PROVIDER_CHAINS_SETTING_MAX_CHARS,
|
|
19490
20359
|
PROVIDER_CHAIN_LEADS: () => PROVIDER_CHAIN_LEADS,
|
|
20360
|
+
PROVIDER_CHAIN_MAX_ENTRIES: () => PROVIDER_CHAIN_MAX_ENTRIES,
|
|
19491
20361
|
PROVIDER_HOST_NOT_OFFERED: () => PROVIDER_HOST_NOT_OFFERED,
|
|
19492
20362
|
PUBLIC_PLANS: () => PUBLIC_PLANS,
|
|
19493
20363
|
QUARANTINE_CLOSE_TAGS: () => QUARANTINE_CLOSE_TAGS,
|
|
@@ -19495,10 +20365,12 @@ __export(dist_exports, {
|
|
|
19495
20365
|
READ_ONLY_TAGS: () => READ_ONLY_TAGS,
|
|
19496
20366
|
RECHECKED_NOTES: () => RECHECKED_NOTES,
|
|
19497
20367
|
RECORDING_VALUE_PLACEHOLDERS: () => RECORDING_VALUE_PLACEHOLDERS,
|
|
20368
|
+
RENDER_IDENTITY_MAX_LENGTH: () => RENDER_IDENTITY_MAX_LENGTH,
|
|
19498
20369
|
REPOSITORY_CLIS: () => REPOSITORY_CLIS,
|
|
19499
20370
|
RESERVED_PROJECT_KEYS: () => RESERVED_PROJECT_KEYS,
|
|
19500
20371
|
RESULT_STRUCTURE_TOO_COSTLY_CODE: () => RESULT_STRUCTURE_TOO_COSTLY_CODE,
|
|
19501
20372
|
RETRIED_ATTEMPTS_KEY: () => RETRIED_ATTEMPTS_KEY,
|
|
20373
|
+
RETRY_HELD_RUN_ERROR: () => RETRY_HELD_RUN_ERROR,
|
|
19502
20374
|
RUNNER_INTERRUPT_ERROR_PREFIX: () => RUNNER_INTERRUPT_ERROR_PREFIX,
|
|
19503
20375
|
RUN_INTERRUPTED_ERROR_PREFIXES: () => RUN_INTERRUPTED_ERROR_PREFIXES,
|
|
19504
20376
|
RUN_INTERRUPTION_NOT_RETRIED_MARKERS: () => RUN_INTERRUPTION_NOT_RETRIED_MARKERS,
|
|
@@ -19511,6 +20383,8 @@ __export(dist_exports, {
|
|
|
19511
20383
|
SHARED_SERVER_APPLICATION_NAME: () => SHARED_SERVER_APPLICATION_NAME,
|
|
19512
20384
|
SPEND_SOURCE_KEY: () => SPEND_SOURCE_KEY,
|
|
19513
20385
|
SPEND_SOURCE_SERVER: () => SPEND_SOURCE_SERVER,
|
|
20386
|
+
STATED_LIMITS_VERSION: () => STATED_LIMITS_VERSION,
|
|
20387
|
+
STATED_PATTERN_MAX: () => STATED_PATTERN_MAX,
|
|
19514
20388
|
STATIC_STANDARD_MIN_INTERACTIVE: () => STATIC_STANDARD_MIN_INTERACTIVE,
|
|
19515
20389
|
STT_MODEL_OPTIONS: () => STT_MODEL_OPTIONS,
|
|
19516
20390
|
SUCCESS_NOTICE_TEXT: () => SUCCESS_NOTICE_TEXT,
|
|
@@ -19540,14 +20414,20 @@ __export(dist_exports, {
|
|
|
19540
20414
|
UI_VARIANTS: () => UI_VARIANTS,
|
|
19541
20415
|
UNEXPECTED_PASS_ERROR_PREFIX: () => UNEXPECTED_PASS_ERROR_PREFIX,
|
|
19542
20416
|
USER_CANCELLED_RUN_ERROR: () => USER_CANCELLED_RUN_ERROR,
|
|
20417
|
+
VISUAL_MASK_SELECTORS_MAX: () => VISUAL_MASK_SELECTORS_MAX,
|
|
20418
|
+
VISUAL_MASK_SELECTOR_FORBIDDEN: () => VISUAL_MASK_SELECTOR_FORBIDDEN,
|
|
20419
|
+
VISUAL_MASK_SELECTOR_MAX_LENGTH: () => VISUAL_MASK_SELECTOR_MAX_LENGTH,
|
|
20420
|
+
VISUAL_V1_IDENTITY: () => VISUAL_V1_IDENTITY,
|
|
19543
20421
|
VOICE_PREVIEW_SAMPLE: () => VOICE_PREVIEW_SAMPLE,
|
|
19544
20422
|
WEB_PROXY_CHAIN_HEADER: () => WEB_PROXY_CHAIN_HEADER,
|
|
19545
20423
|
WITHHELD_CREDENTIAL_TEXT: () => WITHHELD_CREDENTIAL_TEXT,
|
|
19546
20424
|
ZAI_MODEL_PATTERN: () => ZAI_MODEL_PATTERN,
|
|
19547
20425
|
ZAI_PREFIX: () => ZAI_PREFIX,
|
|
20426
|
+
accountServesModel: () => accountServesModel,
|
|
19548
20427
|
accumulateStatements: () => accumulateStatements,
|
|
19549
20428
|
adoptBrokerFailover: () => adoptBrokerFailover,
|
|
19550
20429
|
aiCostToCreditCents: () => aiCostToCreditCents,
|
|
20430
|
+
allowedProviderChainHosts: () => allowedProviderChainHosts,
|
|
19551
20431
|
alternatesAtTop: () => alternatesAtTop,
|
|
19552
20432
|
analyzeDerivedValues: () => analyzeDerivedValues,
|
|
19553
20433
|
analyzeMobileSelectorQuality: () => analyzeMobileSelectorQuality,
|
|
@@ -19560,6 +20440,7 @@ __export(dist_exports, {
|
|
|
19560
20440
|
analyzeUnreachedUrlWaits: () => analyzeUnreachedUrlWaits,
|
|
19561
20441
|
analyzeUrlWaits: () => analyzeUrlWaits,
|
|
19562
20442
|
analyzeWriteResponse: () => analyzeWriteResponse,
|
|
20443
|
+
androidResourceOwners: () => androidResourceOwners,
|
|
19563
20444
|
apiModelCardsFromConfig: () => apiModelCardsFromConfig,
|
|
19564
20445
|
apiModelsForProvider: () => apiModelsForProvider,
|
|
19565
20446
|
apiProviderModelLabel: () => apiProviderModelLabel,
|
|
@@ -19570,6 +20451,7 @@ __export(dist_exports, {
|
|
|
19570
20451
|
applyLaunchSpecialPlan: () => applyLaunchSpecialPlan,
|
|
19571
20452
|
asPlainExpect: () => asPlainExpect,
|
|
19572
20453
|
assertionOnlyCode: () => assertionOnlyCode,
|
|
20454
|
+
assertionStrengthHoistedReadsOn: () => assertionStrengthHoistedReadsOn,
|
|
19573
20455
|
assertionsReadVisibleContent: () => assertionsReadVisibleContent,
|
|
19574
20456
|
assessCodeOutcomeCoverage: () => assessCodeOutcomeCoverage,
|
|
19575
20457
|
assessTitleCoverage: () => assessTitleCoverage,
|
|
@@ -19653,6 +20535,7 @@ __export(dist_exports, {
|
|
|
19653
20535
|
deepgramSpeak: () => deepgramSpeak,
|
|
19654
20536
|
deepgramTranscribe: () => deepgramTranscribe,
|
|
19655
20537
|
defaultConfirmModel: () => defaultConfirmModel,
|
|
20538
|
+
defaultProviderChainEntries: () => defaultProviderChainEntries,
|
|
19656
20539
|
defaultScreenModel: () => defaultScreenModel,
|
|
19657
20540
|
defaultVoiceForTtsModel: () => defaultVoiceForTtsModel,
|
|
19658
20541
|
deriveAuthPlan: () => deriveAuthPlan,
|
|
@@ -19695,6 +20578,7 @@ __export(dist_exports, {
|
|
|
19695
20578
|
extractOutcomeSearchTerms: () => extractOutcomeSearchTerms,
|
|
19696
20579
|
featureForAiCallType: () => featureForAiCallType,
|
|
19697
20580
|
featureForMcpTool: () => featureForMcpTool,
|
|
20581
|
+
featureRowRunStatus: () => featureRowRunStatus,
|
|
19698
20582
|
findBracedClause: () => findBracedClause,
|
|
19699
20583
|
findCounterIdPins: () => findCounterIdPins,
|
|
19700
20584
|
findCredentialTextLeftover: () => findCredentialTextLeftover,
|
|
@@ -19743,6 +20627,7 @@ __export(dist_exports, {
|
|
|
19743
20627
|
getPlanChangeType: () => getPlanChangeType,
|
|
19744
20628
|
getPlanDefinition: () => getPlanDefinition,
|
|
19745
20629
|
getPlanLabel: () => getPlanLabel,
|
|
20630
|
+
getProviderChainOverrides: () => getProviderChainOverrides,
|
|
19746
20631
|
getTrialDaysRemaining: () => getTrialDaysRemaining,
|
|
19747
20632
|
googleBrokerNavigationUrl: () => googleBrokerNavigationUrl,
|
|
19748
20633
|
googleBrokerUnavailableLine: () => googleBrokerUnavailableLine,
|
|
@@ -19756,10 +20641,10 @@ __export(dist_exports, {
|
|
|
19756
20641
|
hasEncodedRequestBody: () => hasEncodedRequestBody,
|
|
19757
20642
|
hasEngineSpecParts: () => hasEngineSpecParts,
|
|
19758
20643
|
hasOnlyConstantAssertions: () => hasOnlyConstantAssertions,
|
|
19759
|
-
hasOpenCodeGo2Prefix: () => hasOpenCodeGo2Prefix,
|
|
19760
20644
|
hasOpenCodeGoPrefix: () => hasOpenCodeGoPrefix,
|
|
19761
20645
|
hasOpenRouterPrefix: () => hasOpenRouterPrefix,
|
|
19762
20646
|
hasPlatformLocator: () => hasPlatformLocator,
|
|
20647
|
+
hasProviderChainLeadsByDefault: () => hasProviderChainLeadsByDefault,
|
|
19763
20648
|
hasReadOnlyMarker: () => hasReadOnlyMarker,
|
|
19764
20649
|
hasSurveyNotes: () => hasSurveyNotes,
|
|
19765
20650
|
hasTestFailContract: () => hasTestFailContract,
|
|
@@ -19785,8 +20670,11 @@ __export(dist_exports, {
|
|
|
19785
20670
|
isAuthTokenName: () => isAuthTokenName,
|
|
19786
20671
|
isBlockedEnvVar: () => isBlockedEnvVar,
|
|
19787
20672
|
isBrowserCliEngine: () => isBrowserCliEngine,
|
|
20673
|
+
isBrowserVersion: () => isBrowserVersion,
|
|
19788
20674
|
isBugTestBrokenError: () => isBugTestBrokenError,
|
|
20675
|
+
isCancelledReason: () => isCancelledReason,
|
|
19789
20676
|
isCanvasUnsupportedMessage: () => isCanvasUnsupportedMessage,
|
|
20677
|
+
isChainablePrimary: () => isChainablePrimary,
|
|
19790
20678
|
isCliEngineModelId: () => isCliEngineModelId,
|
|
19791
20679
|
isCliEngineSpec: () => isCliEngineSpec,
|
|
19792
20680
|
isCodeFactNote: () => isCodeFactNote,
|
|
@@ -19810,6 +20698,7 @@ __export(dist_exports, {
|
|
|
19810
20698
|
isIdentityMobileXpath: () => isIdentityMobileXpath,
|
|
19811
20699
|
isIdentityProviderSurface: () => isIdentityProviderSurface,
|
|
19812
20700
|
isIncidentalAssetPath: () => isIncidentalAssetPath,
|
|
20701
|
+
isIntentionalNegativeAuthTest: () => isIntentionalNegativeAuthTest,
|
|
19813
20702
|
isIpOrLocalhost: () => isIpOrLocalhost,
|
|
19814
20703
|
isKnownPageType: () => isKnownPageType,
|
|
19815
20704
|
isLaunchSpecialActive: () => isLaunchSpecialActive,
|
|
@@ -19837,6 +20726,7 @@ __export(dist_exports, {
|
|
|
19837
20726
|
isRunInterruptedError: () => isRunInterruptedError,
|
|
19838
20727
|
isRunnableStep: () => isRunnableStep,
|
|
19839
20728
|
isSafeDeepLink: () => isSafeDeepLink,
|
|
20729
|
+
isSafeMaskSelector: () => isSafeMaskSelector,
|
|
19840
20730
|
isSameRegistrableDomainHost: () => isSameRegistrableDomainHost,
|
|
19841
20731
|
isSelectableApiModel: () => isSelectableApiModel,
|
|
19842
20732
|
isSelfServePlan: () => isSelfServePlan,
|
|
@@ -19900,6 +20790,7 @@ __export(dist_exports, {
|
|
|
19900
20790
|
normalizeFinishReason: () => normalizeFinishReason,
|
|
19901
20791
|
normalizeNoteText: () => normalizeNoteText,
|
|
19902
20792
|
normalizePageType: () => normalizePageType,
|
|
20793
|
+
normalizeProviderAccountModels: () => normalizeProviderAccountModels,
|
|
19903
20794
|
normalizeTtsModel: () => normalizeTtsModel,
|
|
19904
20795
|
normalizeTtsVoiceId: () => normalizeTtsVoiceId,
|
|
19905
20796
|
noteChatFailover: () => noteChatFailover,
|
|
@@ -19924,13 +20815,18 @@ __export(dist_exports, {
|
|
|
19924
20815
|
parseGeminiModelsPayload: () => parseGeminiModelsPayload,
|
|
19925
20816
|
parseGoogleAuthorizeUrl: () => parseGoogleAuthorizeUrl,
|
|
19926
20817
|
parseGoogleGisRequestUrl: () => parseGoogleGisRequestUrl,
|
|
20818
|
+
parseLastActionProofMode: () => parseLastActionProofMode,
|
|
19927
20819
|
parseLimitParam: () => parseLimitParam,
|
|
19928
20820
|
parseOffsetParam: () => parseOffsetParam,
|
|
19929
20821
|
parsePageParam: () => parsePageParam,
|
|
19930
20822
|
parsePgIntSetting: () => parsePgIntSetting,
|
|
19931
20823
|
parseProjectEngineOverrides: () => parseProjectEngineOverrides,
|
|
19932
20824
|
parsePromotionGateMode: () => parsePromotionGateMode,
|
|
20825
|
+
parseProviderAccountLists: () => parseProviderAccountLists,
|
|
20826
|
+
parseProviderChainOverrides: () => parseProviderChainOverrides,
|
|
19933
20827
|
parseRecheckClearMode: () => parseRecheckClearMode,
|
|
20828
|
+
parseRenderIdentity: () => parseRenderIdentity,
|
|
20829
|
+
parseStoredProviderChainOverrides: () => parseStoredProviderChainOverrides,
|
|
19934
20830
|
parseSurveyEntryUrls: () => parseSurveyEntryUrls,
|
|
19935
20831
|
parseSuspiciousPassMode: () => parseSuspiciousPassMode,
|
|
19936
20832
|
parseTaskModelUpdate: () => parseTaskModelUpdate,
|
|
@@ -19961,7 +20857,9 @@ __export(dist_exports, {
|
|
|
19961
20857
|
projectAppHosts: () => projectAppHosts,
|
|
19962
20858
|
providerChainFor: () => providerChainFor,
|
|
19963
20859
|
providerChainHostOf: () => providerChainHostOf,
|
|
20860
|
+
providerChainHostProvider: () => providerChainHostProvider,
|
|
19964
20861
|
providerChainLeadsFor: () => providerChainLeadsFor,
|
|
20862
|
+
providerChainModelKey: () => providerChainModelKey,
|
|
19965
20863
|
providerChainPrimariesForHop: () => providerChainPrimariesForHop,
|
|
19966
20864
|
providerHostPlan: () => providerHostPlan,
|
|
19967
20865
|
readBalanced: () => readBalanced,
|
|
@@ -19971,6 +20869,7 @@ __export(dist_exports, {
|
|
|
19971
20869
|
readEnabledEngines: () => readEnabledEngines,
|
|
19972
20870
|
readProjectEngineOverrides: () => readProjectEngineOverrides,
|
|
19973
20871
|
readRetriedAttempts: () => readRetriedAttempts,
|
|
20872
|
+
readStatedLimits: () => readStatedLimits,
|
|
19974
20873
|
readStaticPageCanvasSignals: () => readStaticPageCanvasSignals,
|
|
19975
20874
|
readSurveyEntryUrls: () => readSurveyEntryUrls,
|
|
19976
20875
|
readSurveyNotes: () => readSurveyNotes,
|
|
@@ -19983,6 +20882,7 @@ __export(dist_exports, {
|
|
|
19983
20882
|
regexLiteralEnd: () => regexLiteralEnd2,
|
|
19984
20883
|
regexOpensAfter: () => regexOpensAfter,
|
|
19985
20884
|
registrableDomain: () => registrableDomain,
|
|
20885
|
+
renderIdentity: () => renderIdentity,
|
|
19986
20886
|
renderSurveyNotesForPrompt: () => renderSurveyNotesForPrompt,
|
|
19987
20887
|
repairPageType: () => repairPageType,
|
|
19988
20888
|
replaceBlockComments: () => replaceBlockComments,
|
|
@@ -20030,11 +20930,14 @@ __export(dist_exports, {
|
|
|
20030
20930
|
selectorCandidatesForStep: () => selectorCandidatesForStep,
|
|
20031
20931
|
selectorForValue: () => selectorForValue,
|
|
20032
20932
|
setAgenticCliRuntime: () => setAgenticCliRuntime,
|
|
20933
|
+
setProviderChainOverrides: () => setProviderChainOverrides,
|
|
20033
20934
|
signupPlanFor: () => signupPlanFor,
|
|
20034
20935
|
simplifyMobileWanderingInPlace: () => simplifyMobileWanderingInPlace,
|
|
20035
20936
|
siteOf: () => siteOf,
|
|
20036
20937
|
specItemsFromTestPlans: () => specItemsFromTestPlans,
|
|
20037
20938
|
splitAnswerLines: () => splitAnswerLines,
|
|
20939
|
+
statedLimitsFromAttrs: () => statedLimitsFromAttrs,
|
|
20940
|
+
statedLimitsRecord: () => statedLimitsRecord,
|
|
20038
20941
|
stepAppliesToPlatform: () => stepAppliesToPlatform,
|
|
20039
20942
|
stepConsoleLines: () => stepConsoleLines,
|
|
20040
20943
|
stepIsDivergentForPlatform: () => stepIsDivergentForPlatform,
|
|
@@ -20046,7 +20949,10 @@ __export(dist_exports, {
|
|
|
20046
20949
|
stripCodeFactPrefix: () => stripCodeFactPrefix,
|
|
20047
20950
|
stripCoverageComments: () => stripCoverageComments,
|
|
20048
20951
|
stripEnvironmentalPrefix: () => stripEnvironmentalPrefix,
|
|
20952
|
+
stripFractionZeros: () => stripFractionZeros,
|
|
20953
|
+
stripLineComment: () => stripLineComment,
|
|
20049
20954
|
stripLoginScaffolding: () => stripLoginScaffolding,
|
|
20955
|
+
stripTrailingChars: () => stripTrailingChars,
|
|
20050
20956
|
stripTrailingSlashes: () => stripTrailingSlashes,
|
|
20051
20957
|
strongerNeedsReviewSource: () => strongerNeedsReviewSource,
|
|
20052
20958
|
sttModelsForProvider: () => sttModelsForProvider,
|
|
@@ -20064,10 +20970,14 @@ __export(dist_exports, {
|
|
|
20064
20970
|
ttsFallbackModel: () => ttsFallbackModel,
|
|
20065
20971
|
ttsProviderForModel: () => ttsProviderForModel,
|
|
20066
20972
|
unexpectedPassError: () => unexpectedPassError,
|
|
20973
|
+
v1RenderIdentity: () => v1RenderIdentity,
|
|
20067
20974
|
validateAppMapDocument: () => validateAppMapDocument,
|
|
20068
20975
|
validateAssertionStrength: () => validateAssertionStrength,
|
|
20069
20976
|
validateEngineSpec: () => validateEngineSpec,
|
|
20977
|
+
validateProviderChainEntries: () => validateProviderChainEntries,
|
|
20978
|
+
validateProviderChainsSetting: () => validateProviderChainsSetting,
|
|
20070
20979
|
validateTaskModelId: () => validateTaskModelId,
|
|
20980
|
+
visualBaselinesEnabled: () => visualBaselinesEnabled,
|
|
20071
20981
|
visualProDailyBudget: () => visualProDailyBudget,
|
|
20072
20982
|
voiceMatchesTtsModel: () => voiceMatchesTtsModel,
|
|
20073
20983
|
voicesForTtsModel: () => voicesForTtsModel,
|
|
@@ -20130,11 +21040,14 @@ var init_dist = __esm({
|
|
|
20130
21040
|
init_deepgram_voices();
|
|
20131
21041
|
init_feature_flags();
|
|
20132
21042
|
init_env_flags();
|
|
21043
|
+
init_visual_compare_limits();
|
|
21044
|
+
init_visual_baseline_identity();
|
|
20133
21045
|
init_analytics_url();
|
|
20134
21046
|
init_agentic_cli_runtime();
|
|
20135
21047
|
init_api_model_catalog();
|
|
20136
21048
|
init_engine_spec();
|
|
20137
21049
|
init_provider_chains();
|
|
21050
|
+
init_provider_accounts();
|
|
20138
21051
|
init_project_engine_overrides();
|
|
20139
21052
|
init_ai_profiles();
|
|
20140
21053
|
init_openrouter_usage();
|
|
@@ -20176,6 +21089,7 @@ var init_dist = __esm({
|
|
|
20176
21089
|
init_regex_alternation();
|
|
20177
21090
|
init_page_evidence();
|
|
20178
21091
|
init_trailing_slashes();
|
|
21092
|
+
init_stated_limits();
|
|
20179
21093
|
}
|
|
20180
21094
|
});
|
|
20181
21095
|
|
|
@@ -20263,7 +21177,7 @@ var require_runs_dir = __commonJS({
|
|
|
20263
21177
|
} catch (error2) {
|
|
20264
21178
|
if (error2.code === "EEXIST" && resolvesFrom(runsDir) === own)
|
|
20265
21179
|
return;
|
|
20266
|
-
throw new Error(`Could not link ${link} to the runner's node_modules (${error2.message}). Set RUNS_DIR to a writable folder outside node_modules that can resolve @playwright/test
|
|
21180
|
+
throw new Error(`Could not link ${link} to the runner's node_modules (${error2.message}). Set RUNS_DIR to a writable folder outside node_modules that can resolve @playwright/test.`, { cause: error2 });
|
|
20267
21181
|
}
|
|
20268
21182
|
}
|
|
20269
21183
|
}
|
|
@@ -21866,23 +22780,41 @@ var require_harness_init_script = __commonJS({
|
|
|
21866
22780
|
exports2.isHarnessInitConsoleLine = isHarnessInitConsoleLine;
|
|
21867
22781
|
exports2.stripHarnessConsoleEntries = stripHarnessConsoleEntries;
|
|
21868
22782
|
var node_crypto_1 = require("crypto");
|
|
22783
|
+
var shared_1 = (init_dist(), __toCommonJS(dist_exports));
|
|
21869
22784
|
exports2.HARNESS_INIT_SOURCE_URL = `vqa-harness-init-${(0, node_crypto_1.randomBytes)(4).toString("hex")}.js`;
|
|
22785
|
+
var TRANSITION_LOOP_LIMIT = 8;
|
|
22786
|
+
var TRANSITION_LOOP_WINDOW_MS = 1e3;
|
|
21870
22787
|
function withHarnessSourceUrl(content) {
|
|
21871
22788
|
return `${content}
|
|
21872
22789
|
//# sourceURL=${exports2.HARNESS_INIT_SOURCE_URL}`;
|
|
21873
22790
|
}
|
|
21874
22791
|
function buildAnimationDisablingSource(opts) {
|
|
22792
|
+
const finishTransitions = opts.finishTransitions ?? (0, shared_1.parseBoolFlag)(process.env.HARNESS_FINISH_CSS_TRANSITIONS);
|
|
22793
|
+
const css = finishTransitions ? "*, *::before, *::after { animation-duration: 0s !important; animation-delay: 0s !important; }" : "*, *::before, *::after { transition-duration: 0s !important; animation-duration: 0s !important; transition-delay: 0s !important; animation-delay: 0s !important; }";
|
|
21875
22794
|
return withHarnessSourceUrl(`(() => {
|
|
21876
22795
|
// Layer 1: CSS transitions and animations - instant, not removed
|
|
21877
22796
|
// (duration 0s preserves final state; 'none' would prevent keyframes from completing)
|
|
22797
|
+
// The rule goes in a constructed stylesheet on document.adoptedStyleSheets, which adds no node to the DOM:
|
|
22798
|
+
// a <style> element changed what the test sees (locator('style') and 'head > *' counts, document.styleSheets,
|
|
22799
|
+
// an iframe's empty head), so a spec stock Playwright passes failed. The <style> is the fallback for a
|
|
22800
|
+
// browser with no constructable stylesheets.
|
|
22801
|
+
let adopted = false;
|
|
21878
22802
|
try {
|
|
22803
|
+
if (typeof CSSStyleSheet === 'function' && typeof Document === 'function' && 'adoptedStyleSheets' in Document.prototype) {
|
|
22804
|
+
const sheet = new CSSStyleSheet();
|
|
22805
|
+
sheet.replaceSync(${JSON.stringify(css)});
|
|
22806
|
+
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
|
22807
|
+
adopted = true;
|
|
22808
|
+
}
|
|
22809
|
+
} catch (e) {}
|
|
22810
|
+
if (!adopted) try {
|
|
21879
22811
|
let applied = false;
|
|
21880
22812
|
const apply = () => {
|
|
21881
22813
|
if (applied) return true;
|
|
21882
22814
|
const root = document.head || document.documentElement;
|
|
21883
22815
|
if (!root) return false;
|
|
21884
22816
|
const style = document.createElement('style');
|
|
21885
|
-
style.textContent =
|
|
22817
|
+
style.textContent = ${JSON.stringify(css)};
|
|
21886
22818
|
root.appendChild(style);
|
|
21887
22819
|
applied = true;
|
|
21888
22820
|
return true;
|
|
@@ -21904,6 +22836,48 @@ ${opts.hideWebdriver ? `
|
|
|
21904
22836
|
try {
|
|
21905
22837
|
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
|
|
21906
22838
|
} catch (e) {}
|
|
22839
|
+
` : ""}
|
|
22840
|
+
${finishTransitions ? `
|
|
22841
|
+
// Layer 1b: CSS transitions run, and are finished as they start, so transitionend fires.
|
|
22842
|
+
// - finish() is deferred to a task: called inside the transitionrun dispatch (or a microtask of it) after a
|
|
22843
|
+
// real input event, Chromium finishes the transition but never fires its transitionstart/transitionend.
|
|
22844
|
+
// - setTimeout and performance.now are the ones present when this script runs, before the page's own
|
|
22845
|
+
// scripts: fake timers the page installs later cannot hold a finish back.
|
|
22846
|
+
// - The subtree is walked only for a pseudo-element's transition (its animations hang off the element).
|
|
22847
|
+
// - An element whose same transition restarts more than ${TRANSITION_LOOP_LIMIT} times within ${TRANSITION_LOOP_WINDOW_MS} ms (an app that starts it again on
|
|
22848
|
+
// each transitionend: carousel, pulse) is left to run at its own pace from then on, instead of cycling at
|
|
22849
|
+
// frame rate for the rest of the run.
|
|
22850
|
+
try {
|
|
22851
|
+
const st = setTimeout;
|
|
22852
|
+
const perf = typeof performance === 'object' && performance && typeof performance.now === 'function' ? performance : null;
|
|
22853
|
+
const now = perf ? perf.now.bind(perf) : Date.now;
|
|
22854
|
+
const seen = new WeakMap();
|
|
22855
|
+
const looping = (el, key) => {
|
|
22856
|
+
let byKey = seen.get(el);
|
|
22857
|
+
if (!byKey) { byKey = new Map(); seen.set(el, byKey); }
|
|
22858
|
+
const t = now();
|
|
22859
|
+
let s = byKey.get(key);
|
|
22860
|
+
if (s && s.loop) return true;
|
|
22861
|
+
if (!s || t - s.since > ${TRANSITION_LOOP_WINDOW_MS}) { s = { n: 0, since: t, loop: false }; byKey.set(key, s); }
|
|
22862
|
+
s.n += 1;
|
|
22863
|
+
if (s.n > ${TRANSITION_LOOP_LIMIT}) s.loop = true;
|
|
22864
|
+
return s.loop;
|
|
22865
|
+
};
|
|
22866
|
+
window.addEventListener('transitionrun', (ev) => {
|
|
22867
|
+
try {
|
|
22868
|
+
const el = ev && ev.target;
|
|
22869
|
+
if (!el || typeof el.getAnimations !== 'function' || typeof CSSTransition !== 'function') return;
|
|
22870
|
+
const pseudo = ev.pseudoElement || '';
|
|
22871
|
+
if (looping(el, ev.propertyName + '|' + pseudo)) return;
|
|
22872
|
+
for (const a of (pseudo ? el.getAnimations({ subtree: true }) : el.getAnimations())) {
|
|
22873
|
+
if (a instanceof CSSTransition && a.transitionProperty === ev.propertyName && a.effect
|
|
22874
|
+
&& a.effect.target === el && (a.effect.pseudoElement || '') === pseudo) {
|
|
22875
|
+
st(() => { try { a.finish(); } catch (e) {} }, 0);
|
|
22876
|
+
}
|
|
22877
|
+
}
|
|
22878
|
+
} catch (e) {}
|
|
22879
|
+
}, true);
|
|
22880
|
+
} catch (e) {}
|
|
21907
22881
|
` : ""}
|
|
21908
22882
|
// Layer 2: Web Animations API - make instant instead of no-op
|
|
21909
22883
|
// (preserves animation completion callbacks that app code may depend on)
|
|
@@ -22795,6 +23769,7 @@ var require_browser_config = __commonJS({
|
|
|
22795
23769
|
exports2.fullChromiumCloudLaunch = fullChromiumCloudLaunch;
|
|
22796
23770
|
exports2.chooseCloudBrowser = chooseCloudBrowser;
|
|
22797
23771
|
exports2.appTimezoneConfigLine = appTimezoneConfigLine;
|
|
23772
|
+
exports2.discoveryTimezoneId = discoveryTimezoneId;
|
|
22798
23773
|
exports2.getMCPBrowserConfig = getMCPBrowserConfig;
|
|
22799
23774
|
exports2.getPlaywrightLaunchOptions = getPlaywrightLaunchOptions;
|
|
22800
23775
|
exports2.getLaunchOptions = getLaunchOptions;
|
|
@@ -23013,7 +23988,7 @@ var require_browser_config = __commonJS({
|
|
|
23013
23988
|
try {
|
|
23014
23989
|
(0, fs_1.cpSync)(localStateSrc, (0, path_1.join)(tempDir, "Local State"));
|
|
23015
23990
|
} catch (err) {
|
|
23016
|
-
throw new Error(`Cannot copy Chrome profile (${err instanceof Error ? err.message : String(err)}). Quit Google Chrome completely, or use getChromeUserDataDirSource() for seeding
|
|
23991
|
+
throw new Error(`Cannot copy Chrome profile (${err instanceof Error ? err.message : String(err)}). Quit Google Chrome completely, or use getChromeUserDataDirSource() for seeding.`, { cause: err });
|
|
23017
23992
|
}
|
|
23018
23993
|
}
|
|
23019
23994
|
for (const file of essentialFiles) {
|
|
@@ -23204,6 +24179,10 @@ var require_browser_config = __commonJS({
|
|
|
23204
24179
|
const zone = (0, shared_1.appTimezoneFromVariables)(vars);
|
|
23205
24180
|
return zone ? `timezoneId: ${JSON.stringify(zone)},` : "";
|
|
23206
24181
|
}
|
|
24182
|
+
function discoveryTimezoneId(ctx) {
|
|
24183
|
+
const vars = ctx.variableContext;
|
|
24184
|
+
return (0, shared_1.appTimezoneFromVariables)(vars?.publicTestVariables) ?? (0, shared_1.appTimezoneFromVariables)(vars?.allTestVariables);
|
|
24185
|
+
}
|
|
23207
24186
|
function getMCPBrowserConfig(opts) {
|
|
23208
24187
|
const userDataDir = opts?.forceIsolated ? null : getTestUserDataDir();
|
|
23209
24188
|
const timezoneOption = opts?.timezoneId ? { timezoneId: opts.timezoneId } : {};
|
|
@@ -23990,7 +24969,7 @@ var require_survey_door_harvester = __commonJS({
|
|
|
23990
24969
|
}
|
|
23991
24970
|
if (!payload)
|
|
23992
24971
|
continue;
|
|
23993
|
-
const cleaned = payload.replace(/^\s*```(json)?\s*\n
|
|
24972
|
+
const cleaned = payload.replace(/^\s*```(json)?\s*\n?/i, "").replace(/\n?```\s*$/, "").trim();
|
|
23994
24973
|
if (!cleaned)
|
|
23995
24974
|
continue;
|
|
23996
24975
|
try {
|