@validate.qa/runner 1.0.100 → 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-MBKGTELT.mjs → chunk-3JM6XTSZ.mjs} +1862 -530
- package/dist/{chunk-2XD63NU5.mjs → chunk-4WLP3RWN.mjs} +941 -86
- package/dist/{chunk-MHFP3QXU.mjs → chunk-5DFBEL72.mjs} +1 -1
- package/dist/{chunk-II7QW7MZ.mjs → chunk-BXLEPT4M.mjs} +31 -18
- package/dist/{chunk-MAO6ADAO.mjs → chunk-CBVHPYVJ.mjs} +62 -2
- package/dist/{chunk-5P4ANVDY.mjs → chunk-DN3O6EYT.mjs} +43073 -23357
- package/dist/{chunk-GIIIVO2W.mjs → chunk-E2RRAE27.mjs} +155 -11
- package/dist/chunk-E2VEMBQQ.mjs +291 -0
- package/dist/{chunk-HMUC3DKS.mjs → chunk-HJ6FKHMM.mjs} +2281 -2
- package/dist/{chunk-WDXLAZ2J.mjs → chunk-PWFWMVZU.mjs} +3 -2
- package/dist/{chunk-6NIT2CCH.mjs → chunk-ZZYBRIBT.mjs} +18 -12
- package/dist/cli.js +139723 -114754
- package/dist/cli.mjs +536 -247
- package/dist/credential-mcp-server.js +1761 -358
- package/dist/credential-mcp-server.mjs +3 -3
- package/dist/discovery-mcp-server.js +1874 -379
- package/dist/discovery-mcp-server.mjs +4 -4
- package/dist/dist-52M5RDZ6.mjs +17 -0
- package/dist/inbox-mcp-server.js +1902 -505
- package/dist/inbox-mcp-server.mjs +3 -3
- package/dist/route-seed-worker.js +3976 -356
- package/dist/route-seed-worker.mjs +14 -7
- package/dist/run-write-guard-preload.js +2908 -572
- package/dist/run-write-guard-preload.mjs +5 -285
- package/dist/run-write-guard.js +2908 -572
- 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 +4 -3
- package/dist/dist-DFNLMH6L.mjs +0 -16
|
@@ -105,13 +105,13 @@ function isBlockedEnvVar(key) {
|
|
|
105
105
|
function sanitizeTestVariables(vars) {
|
|
106
106
|
if (!vars)
|
|
107
107
|
return {};
|
|
108
|
-
const
|
|
108
|
+
const clean3 = {};
|
|
109
109
|
for (const [key, value] of Object.entries(vars)) {
|
|
110
110
|
if (!isBlockedEnvVar(key)) {
|
|
111
|
-
|
|
111
|
+
clean3[key] = value;
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
-
return
|
|
114
|
+
return clean3;
|
|
115
115
|
}
|
|
116
116
|
function buildSandboxedEnv(processEnv, testVariables) {
|
|
117
117
|
const env = {};
|
|
@@ -1619,8 +1619,11 @@ var init_discovery_har_decision = __esm({
|
|
|
1619
1619
|
});
|
|
1620
1620
|
|
|
1621
1621
|
// ../shared/dist/run-classification.js
|
|
1622
|
-
function
|
|
1623
|
-
|
|
1622
|
+
function isCancelledReason(error) {
|
|
1623
|
+
if (typeof error !== "string")
|
|
1624
|
+
return false;
|
|
1625
|
+
const text = error.trim();
|
|
1626
|
+
return CANCELLED_REASON_PREFIXES.some((prefix) => text.startsWith(prefix));
|
|
1624
1627
|
}
|
|
1625
1628
|
function isHeldHealRun(run) {
|
|
1626
1629
|
return run?.status === "PASSED" && typeof run.error === "string" && run.error.startsWith(HEAL_PATCH_HELD_RUN_ERROR_PREFIX);
|
|
@@ -1653,7 +1656,7 @@ function pickLatestHealthRun(runs) {
|
|
|
1653
1656
|
for (const run of runs) {
|
|
1654
1657
|
if (run.mode === "auth-setup")
|
|
1655
1658
|
continue;
|
|
1656
|
-
if (run.status === "ERROR" &&
|
|
1659
|
+
if (run.status === "ERROR" && isCancelledReason(run.error))
|
|
1657
1660
|
continue;
|
|
1658
1661
|
if (isHeldHealRun(run))
|
|
1659
1662
|
continue;
|
|
@@ -1671,6 +1674,15 @@ function pickDisplayRun(runs) {
|
|
|
1671
1674
|
return latest;
|
|
1672
1675
|
return pickLatestHealthRun(runs) ?? latest;
|
|
1673
1676
|
}
|
|
1677
|
+
function featureRowRunStatus(runs) {
|
|
1678
|
+
const latest = runs.find((run) => run.mode !== "auth-setup");
|
|
1679
|
+
if (!latest)
|
|
1680
|
+
return null;
|
|
1681
|
+
const decisive = pickLatestHealthRun(runs.filter((run) => !isEnvironmentalError(run)));
|
|
1682
|
+
if (decisive)
|
|
1683
|
+
return decisive.status;
|
|
1684
|
+
return latest.status === "PENDING" || latest.status === "RUNNING" ? latest.status : null;
|
|
1685
|
+
}
|
|
1674
1686
|
function isEnvironmentalError(run) {
|
|
1675
1687
|
if (!run)
|
|
1676
1688
|
return false;
|
|
@@ -1702,7 +1714,7 @@ function classifyErrorMessage(error) {
|
|
|
1702
1714
|
return "config_issue";
|
|
1703
1715
|
if (EMAIL_NOT_RECEIVED_PREFIXES.some((prefix) => normalized.startsWith(prefix)))
|
|
1704
1716
|
return "email_not_received";
|
|
1705
|
-
if (
|
|
1717
|
+
if (isCancelledReason(error))
|
|
1706
1718
|
return "cancelled";
|
|
1707
1719
|
if (isRetriedRunInterruption(error))
|
|
1708
1720
|
return "interrupted";
|
|
@@ -1737,7 +1749,7 @@ function heldHealRunWhere() {
|
|
|
1737
1749
|
function excludeCancelledRunsWhere() {
|
|
1738
1750
|
return {
|
|
1739
1751
|
NOT: [
|
|
1740
|
-
{ status: "ERROR", error: { not: null, startsWith:
|
|
1752
|
+
{ status: "ERROR", OR: CANCELLED_REASON_PREFIXES.map((prefix) => ({ error: { not: null, startsWith: prefix } })) },
|
|
1741
1753
|
heldHealRunWhere()
|
|
1742
1754
|
]
|
|
1743
1755
|
};
|
|
@@ -1815,11 +1827,25 @@ function reportedBugWhere() {
|
|
|
1815
1827
|
function bugReviewStatus(status) {
|
|
1816
1828
|
return status === "QUARANTINED" ? "confirmed" : "needs-review";
|
|
1817
1829
|
}
|
|
1818
|
-
var USER_CANCELLED_RUN_ERROR, HEAL_PATCH_HELD_RUN_ERROR_PREFIX, RUNNER_INTERRUPT_ERROR_PREFIX, SERVER_INTERRUPT_ERROR_PREFIX, RUN_LOST_ERROR_PREFIX, RUN_INTERRUPTED_ERROR_PREFIXES, RUN_INTERRUPTION_RETRIED_MARKER, RUN_INTERRUPTION_NOT_RETRIED_MARKERS, BUG_ASSERTION_TAG, UNEXPECTED_PASS_ERROR_PREFIX, BUG_TEST_BROKEN_ERROR_PREFIX, BUG_TEST_BROKEN_REASON, EMAIL_NOT_RECEIVED_PREFIXES, ENVIRONMENTAL_ERROR_PREFIXES, ENVIRONMENTAL_ERROR_STORED_PREFIXES, BUG_SURFACE_SUPPRESSING_TAGS, QUARANTINE_CLOSE_TAGS, BUG_CANDIDATE_TAG, BUG_TEST_BROKEN_TAG;
|
|
1830
|
+
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;
|
|
1819
1831
|
var init_run_classification = __esm({
|
|
1820
1832
|
"../shared/dist/run-classification.js"() {
|
|
1821
1833
|
"use strict";
|
|
1822
1834
|
USER_CANCELLED_RUN_ERROR = "Cancelled by user";
|
|
1835
|
+
PLATFORM_SUPPORT_CANCELLED_RUN_ERROR = "Cancelled by platform support";
|
|
1836
|
+
PLATFORM_ADMIN_CANCELLED_RUN_ERROR = "Cancelled by platform admin";
|
|
1837
|
+
ADMIN_FORCE_CANCELLED_RUN_ERROR = "Admin force-cancelled all active runs";
|
|
1838
|
+
CHAIN_CANCELLED_RUN_ERROR = "Cancelled: parent run cancelled";
|
|
1839
|
+
RETRY_HELD_RUN_ERROR = "Cancelled: auto generation paused";
|
|
1840
|
+
CANCELLED_REASON_PREFIXES = [
|
|
1841
|
+
USER_CANCELLED_RUN_ERROR,
|
|
1842
|
+
PLATFORM_SUPPORT_CANCELLED_RUN_ERROR,
|
|
1843
|
+
PLATFORM_ADMIN_CANCELLED_RUN_ERROR,
|
|
1844
|
+
ADMIN_FORCE_CANCELLED_RUN_ERROR,
|
|
1845
|
+
CHAIN_CANCELLED_RUN_ERROR,
|
|
1846
|
+
RETRY_HELD_RUN_ERROR
|
|
1847
|
+
];
|
|
1848
|
+
isUserCancelledError = isCancelledReason;
|
|
1823
1849
|
HEAL_PATCH_HELD_RUN_ERROR_PREFIX = "Heal patch held";
|
|
1824
1850
|
RUNNER_INTERRUPT_ERROR_PREFIX = "Runner shutting down";
|
|
1825
1851
|
SERVER_INTERRUPT_ERROR_PREFIX = "Run interrupted by server restart";
|
|
@@ -1835,6 +1861,7 @@ var init_run_classification = __esm({
|
|
|
1835
1861
|
UNEXPECTED_PASS_ERROR_PREFIX = "UNEXPECTED_PASS:";
|
|
1836
1862
|
BUG_TEST_BROKEN_ERROR_PREFIX = "BUG_TEST_BROKEN:";
|
|
1837
1863
|
BUG_TEST_BROKEN_REASON = "bug test broken (not a bug verdict)";
|
|
1864
|
+
TEST_DID_NOT_RUN_ERROR = "Test did not run (a dependency failed or the run was interrupted)";
|
|
1838
1865
|
EMAIL_NOT_RECEIVED_PREFIXES = [
|
|
1839
1866
|
"VERIFICATION_EMAIL_NOT_RECEIVED:",
|
|
1840
1867
|
"VERIFICATION_EMAIL_HAS_NO_CODE:",
|
|
@@ -1847,9 +1874,10 @@ var init_run_classification = __esm({
|
|
|
1847
1874
|
];
|
|
1848
1875
|
ENVIRONMENTAL_ERROR_STORED_PREFIXES = [
|
|
1849
1876
|
...ENVIRONMENTAL_ERROR_PREFIXES.flatMap((prefix) => [prefix, `Error: ${prefix}`]),
|
|
1850
|
-
// Written by the
|
|
1851
|
-
|
|
1877
|
+
// Written by the cancels as is, with no prefix wrapper.
|
|
1878
|
+
...CANCELLED_REASON_PREFIXES
|
|
1852
1879
|
];
|
|
1880
|
+
FEATURE_ROW_RUN_WINDOW = 10;
|
|
1853
1881
|
BUG_SURFACE_SUPPRESSING_TAGS = ["@rate-limit-blocked", "@quota-exceeded", "@auth-lost"];
|
|
1854
1882
|
QUARANTINE_CLOSE_TAGS = [
|
|
1855
1883
|
"@bug-found",
|
|
@@ -2938,176 +2966,6 @@ var init_origin_domain = __esm({
|
|
|
2938
2966
|
}
|
|
2939
2967
|
});
|
|
2940
2968
|
|
|
2941
|
-
// ../shared/dist/auth-block-detector.js
|
|
2942
|
-
function stripEchoLines(text) {
|
|
2943
|
-
return text.split("\n").filter((line) => !ECHO_LINE_RE.test(line)).join("\n");
|
|
2944
|
-
}
|
|
2945
|
-
function matchesAny(text, patterns) {
|
|
2946
|
-
return patterns.some((p) => p.test(text));
|
|
2947
|
-
}
|
|
2948
|
-
function detectAuthBlock(input) {
|
|
2949
|
-
const tags = input.tags ?? [];
|
|
2950
|
-
const isAuthed = tags.some((t) => AUTHED_TAGS.includes(t));
|
|
2951
|
-
if (!isAuthed)
|
|
2952
|
-
return NO_DETECTION;
|
|
2953
|
-
const meta = [input.testName, input.description].filter(Boolean).join(" ");
|
|
2954
|
-
if (matchesAny(meta, INTENTIONAL_NEGATIVE_AUTH))
|
|
2955
|
-
return NO_DETECTION;
|
|
2956
|
-
const sources = [];
|
|
2957
|
-
if (input.error)
|
|
2958
|
-
sources.push(stripEchoLines(input.error));
|
|
2959
|
-
if (input.logs)
|
|
2960
|
-
sources.push(stripEchoLines(input.logs));
|
|
2961
|
-
for (const sr of input.stepResults ?? []) {
|
|
2962
|
-
if (sr.error)
|
|
2963
|
-
sources.push(stripEchoLines(sr.error));
|
|
2964
|
-
}
|
|
2965
|
-
for (const text of sources) {
|
|
2966
|
-
if (!text)
|
|
2967
|
-
continue;
|
|
2968
|
-
for (const sig of AUTH_BLOCK_SIGNATURES) {
|
|
2969
|
-
if (sig.re.test(text)) {
|
|
2970
|
-
return {
|
|
2971
|
-
detected: true,
|
|
2972
|
-
reason: sig.reason,
|
|
2973
|
-
evidence: [`login-block signature "${sig.reason}" in: "${text.slice(0, 140).replace(/\s+/g, " ").trim()}"`]
|
|
2974
|
-
};
|
|
2975
|
-
}
|
|
2976
|
-
}
|
|
2977
|
-
}
|
|
2978
|
-
return NO_DETECTION;
|
|
2979
|
-
}
|
|
2980
|
-
function detectApiColdSessionBlock(input) {
|
|
2981
|
-
const tags = input.tags ?? [];
|
|
2982
|
-
if (!tags.includes("@api"))
|
|
2983
|
-
return NO_DETECTION;
|
|
2984
|
-
const cookieMissing = input.authStateExpected && !input.authStateInjected;
|
|
2985
|
-
const standingMissing = !!input.needsStandingHeaders && !input.standingHeadersInjected;
|
|
2986
|
-
if (!cookieMissing && !standingMissing)
|
|
2987
|
-
return NO_DETECTION;
|
|
2988
|
-
const has401Status = (input.apiCalls ?? []).some((c) => c && (c.status === 401 || c.status === 403));
|
|
2989
|
-
const texts = [];
|
|
2990
|
-
if (input.error)
|
|
2991
|
-
texts.push(input.error);
|
|
2992
|
-
if (input.logs)
|
|
2993
|
-
texts.push(input.logs);
|
|
2994
|
-
for (const sr of input.stepResults ?? [])
|
|
2995
|
-
if (sr.error)
|
|
2996
|
-
texts.push(sr.error);
|
|
2997
|
-
const has401Text = texts.some((t) => UNAUTHORIZED_TEXT_RE.test(t));
|
|
2998
|
-
if (!has401Status && !has401Text)
|
|
2999
|
-
return NO_DETECTION;
|
|
3000
|
-
const missingAuth = cookieMissing ? "cold injected auth session (401/403 with no storageState)" : "missing standing auth header (401/403 with no API_AUTH_HEADERS installed)";
|
|
3001
|
-
const injectedNoun = cookieMissing ? "injected session was absent/expired" : "standing auth header was not installed";
|
|
3002
|
-
return {
|
|
3003
|
-
detected: true,
|
|
3004
|
-
reason: missingAuth,
|
|
3005
|
-
evidence: [
|
|
3006
|
-
has401Status ? `a captured @api call returned 401/403 while the ${injectedNoun}` : `a 401/403 signal in the run error/logs while the ${injectedNoun}`
|
|
3007
|
-
]
|
|
3008
|
-
};
|
|
3009
|
-
}
|
|
3010
|
-
function coldSessionConfigMessage(context, reason, evidence) {
|
|
3011
|
-
return `CONFIG_ISSUE: ${context} without valid injected auth (${reason}) \u2014 no session cookie was injected this run. Ensure the project login (auth-setup recipe / prewarm) is configured so the shared session jar warms (a project with no login: set TEST_USER_EMAIL and TEST_USER_PASSWORD in Test Data \u2192 Test Variables so a session can be captured); for endpoints that authenticate with a standing API key or bearer header instead, set them in the project's API_AUTH_HEADERS variable (injected as request headers). Evidence: ${evidence.join("; ")}`;
|
|
3012
|
-
}
|
|
3013
|
-
var NO_DETECTION, AUTHED_TAGS, LOGIN_CTX, AUTH_BLOCK_SIGNATURES, INTENTIONAL_NEGATIVE_AUTH, ECHO_LINE_RE, UNAUTHORIZED_TEXT_RE;
|
|
3014
|
-
var init_auth_block_detector = __esm({
|
|
3015
|
-
"../shared/dist/auth-block-detector.js"() {
|
|
3016
|
-
"use strict";
|
|
3017
|
-
NO_DETECTION = { detected: false, reason: "", evidence: [] };
|
|
3018
|
-
AUTHED_TAGS = ["@needs-auth", "@inline-login"];
|
|
3019
|
-
LOGIN_CTX = "\\b(?:log\\s*in|login|logging\\s+in|sign[\\s-]?in|signing\\s+in|continue)\\b";
|
|
3020
|
-
AUTH_BLOCK_SIGNATURES = [
|
|
3021
|
-
{ reason: "incorrect password", re: /\b(incorrect|invalid|wrong|bad)\s+password\b/i },
|
|
3022
|
-
{ reason: "password is incorrect", re: /\bpassword\s+(is\s+)?(incorrect|invalid|wrong)\b/i },
|
|
3023
|
-
{ reason: "invalid credentials", re: /\b(invalid|incorrect|wrong|bad)\s+(login\s+)?credentials\b/i },
|
|
3024
|
-
{ reason: "credentials do not match", re: /\bcredentials\b[^\n]{0,40}\b(do(?:es)?\s*n['’]?t|do\s+not)\s+match\b/i },
|
|
3025
|
-
{ reason: "incorrect email or password", re: /\b(incorrect|invalid|wrong)\s+(email|username)\s+or\s+password\b/i },
|
|
3026
|
-
// Require an EXPLICIT negative (codex #21): the old `(not\s+)?(confirmed|verified)` made the negation
|
|
3027
|
-
// optional, so a benign "Email verified" success message / feature assertion matched and hid a real
|
|
3028
|
-
// feature failure as a CONFIG_ISSUE. Only "email not (yet) confirmed/verified" or "unconfirmed/unverified".
|
|
3029
|
-
{ reason: "email not confirmed", re: /\b(e-?mail)\b[^\n]{0,30}\b(not\s+(?:yet\s+)?(?:confirmed|verified)|unconfirmed|unverified)\b/i },
|
|
3030
|
-
{ reason: "please confirm your email", re: /\bplease\s+(confirm|verify)\s+your\s+(e-?mail)\b/i },
|
|
3031
|
-
// The bare "confirm/verify your email" and "account not found" phrases are ALSO ordinary feature copy
|
|
3032
|
-
// ("Verify your email banner", a 404-style "Account not found" page), so they only count with a
|
|
3033
|
-
// login/sign-in context on the SAME line (either side). "Please confirm/verify your email" above
|
|
3034
|
-
// is already an explicit prompt and stays unconditional.
|
|
3035
|
-
{ reason: "confirm your email to continue", re: new RegExp(`${LOGIN_CTX}[^\\n]{0,80}\\bconfirm\\s+your\\s+e-?mail\\b|\\bconfirm\\s+your\\s+e-?mail\\b[^\\n]{0,80}${LOGIN_CTX}`, "i") },
|
|
3036
|
-
{ reason: "verify your email to continue", re: new RegExp(`${LOGIN_CTX}[^\\n]{0,80}\\bverify\\s+your\\s+e-?mail\\b|\\bverify\\s+your\\s+e-?mail\\b[^\\n]{0,80}${LOGIN_CTX}`, "i") },
|
|
3037
|
-
{
|
|
3038
|
-
reason: "account not found",
|
|
3039
|
-
re: new RegExp(`${LOGIN_CTX}[^\\n]{0,80}\\baccount\\b[^\\n]{0,20}\\b(?:not\\s+found|does(?:n['\u2019]t| not)\\s+exist)\\b|\\baccount\\b[^\\n]{0,20}\\b(?:not\\s+found|does(?:n['\u2019]t| not)\\s+exist)\\b[^\\n]{0,80}${LOGIN_CTX}`, "i")
|
|
3040
|
-
},
|
|
3041
|
-
{ reason: "authentication failed", re: /\bauthentication\s+failed\b/i },
|
|
3042
|
-
{ reason: "login failed", re: /\b(login|log\s*in|sign[\s-]?in)\s+failed\b/i }
|
|
3043
|
-
];
|
|
3044
|
-
INTENTIONAL_NEGATIVE_AUTH = [
|
|
3045
|
-
/\b(invalid|incorrect|wrong|bad)\s+(password|credentials|email|login)\b/i,
|
|
3046
|
-
/\b(negative|unhappy)\s+(login|auth|sign[\s-]?in|path|case|flow|test)\b/i,
|
|
3047
|
-
// "should fail" et al must be TIED to an auth noun (codex #21): a bare `should\s+(fail|reject|deny)`
|
|
3048
|
-
// exempted benign names like "Export should fail gracefully", silently disabling the anti-lockout
|
|
3049
|
-
// protection for them. Require a login/auth noun on either side of the "should fail / fail to log in".
|
|
3050
|
-
/\b(login|log\s*in|sign[\s-]?in|auth\w*|credential|password)\b[^\n]{0,40}\bshould\s+(fail|reject|deny)\b/i,
|
|
3051
|
-
/\bshould\s+(fail|reject|deny|not\s+log(?:\s*in)?)\b[^\n]{0,40}\b(login|log\s*in|sign[\s-]?in|auth\w*|credential|password|unauthenticated)\b/i,
|
|
3052
|
-
/\bfailed?\s+login\b/i,
|
|
3053
|
-
/\blogin\s+(error|failure|rejected?)\b/i,
|
|
3054
|
-
/\b(reject|deny|denied|block(ed)?)\b[^\n]{0,20}\b(login|sign[\s-]?in|auth|credential|password)\b/i
|
|
3055
|
-
];
|
|
3056
|
-
ECHO_LINE_RE = /^\s*(?:(?:📋|🎭|🌐|⚙️?)|(?:stderr:\s*|stdout:\s*)?Locator:)/u;
|
|
3057
|
-
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;
|
|
3058
|
-
}
|
|
3059
|
-
});
|
|
3060
|
-
|
|
3061
|
-
// ../shared/dist/skip-coverage-detector.js
|
|
3062
|
-
function detectSkippedApiCoverage(steps) {
|
|
3063
|
-
const allSkipped = steps.length > 0 && steps.every((s) => s.status === "skipped");
|
|
3064
|
-
if (allSkipped) {
|
|
3065
|
-
if (steps.every((s) => s.skipKind === "fixme")) {
|
|
3066
|
-
return {
|
|
3067
|
-
detected: false,
|
|
3068
|
-
logLine: "All @api tests skipped, but EVERY skip is a deliberate fixme (intentional N/A \u2014 e.g. a UI-only redirect login) \u2014 NOT relabelling CONFIG_ISSUE"
|
|
3069
|
-
};
|
|
3070
|
-
}
|
|
3071
|
-
return {
|
|
3072
|
-
detected: true,
|
|
3073
|
-
error: (
|
|
3074
|
-
// Name BOTH auth mechanisms (item 5d): the all-skipped copy used to say only "refresh the auth
|
|
3075
|
-
// state", which misdirects a @auth-standing operator (whose real gap is a missing API_AUTH_HEADERS
|
|
3076
|
-
// key, not a cold cookie session) to warm a login that was never the problem.
|
|
3077
|
-
"CONFIG_ISSUE: Authenticated @api coverage was NOT exercised \u2014 every test skipped (cold injected session, a missing standing auth header, or an unresolved fixme). Provide the auth the endpoints need \u2014 warm the project login/session cookie (auth-setup recipe / prewarm), or set a standing API key/bearer in the project's API_AUTH_HEADERS variable \u2014 so the assertions actually run."
|
|
3078
|
-
),
|
|
3079
|
-
logLine: "All @api tests skipped \u2014 posting ERROR/CONFIG_ISSUE (not PASSED) so zero authenticated coverage is visible, not silent green"
|
|
3080
|
-
};
|
|
3081
|
-
}
|
|
3082
|
-
const runtimeSkipped = steps.filter((s) => s.status === "skipped" && s.skipKind === "skip");
|
|
3083
|
-
if (runtimeSkipped.length === 0)
|
|
3084
|
-
return { detected: false };
|
|
3085
|
-
const reasons = [...new Set(runtimeSkipped.map((s) => `"${s.title ?? "test"}"${s.skipReason ? ` (${s.skipReason})` : ""}`))].join("; ");
|
|
3086
|
-
return {
|
|
3087
|
-
detected: true,
|
|
3088
|
-
error: `CONFIG_ISSUE: ${runtimeSkipped.length} of ${steps.length} @api tests skipped at runtime \u2014 that coverage was NOT exercised. Skipped: ${reasons}`,
|
|
3089
|
-
logLine: `${runtimeSkipped.length}/${steps.length} @api tests skipped at runtime \u2014 posting ERROR/CONFIG_ISSUE (not PASSED) so the coverage hole is visible, not silent green`
|
|
3090
|
-
};
|
|
3091
|
-
}
|
|
3092
|
-
var init_skip_coverage_detector = __esm({
|
|
3093
|
-
"../shared/dist/skip-coverage-detector.js"() {
|
|
3094
|
-
"use strict";
|
|
3095
|
-
}
|
|
3096
|
-
});
|
|
3097
|
-
|
|
3098
|
-
// ../shared/dist/feature-flags.js
|
|
3099
|
-
function isSharedAuthInjectEnabled(value = process.env.SHARED_AUTH_INJECT) {
|
|
3100
|
-
return value?.trim().toLowerCase() !== "false";
|
|
3101
|
-
}
|
|
3102
|
-
function isAuthReadinessGuardEnabled(value = process.env.AUTH_READINESS_GUARD) {
|
|
3103
|
-
return value?.trim().toLowerCase() !== "false";
|
|
3104
|
-
}
|
|
3105
|
-
var init_feature_flags = __esm({
|
|
3106
|
-
"../shared/dist/feature-flags.js"() {
|
|
3107
|
-
"use strict";
|
|
3108
|
-
}
|
|
3109
|
-
});
|
|
3110
|
-
|
|
3111
2969
|
// ../shared/dist/repository-cli.js
|
|
3112
2970
|
function isRepositoryCli(value) {
|
|
3113
2971
|
return typeof value === "string" && REPOSITORY_CLIS.includes(value);
|
|
@@ -3303,9 +3161,40 @@ function semanticTarget(label, platform, base) {
|
|
|
3303
3161
|
}
|
|
3304
3162
|
};
|
|
3305
3163
|
}
|
|
3164
|
+
function androidResourceOwners(value) {
|
|
3165
|
+
const owners = [];
|
|
3166
|
+
for (const match of value.matchAll(ANDROID_RESOURCE_ID_SUFFIX_RE)) {
|
|
3167
|
+
let runStart = match.index;
|
|
3168
|
+
while (runStart > 0 && /[a-z0-9_.]/i.test(value[runStart - 1]))
|
|
3169
|
+
runStart--;
|
|
3170
|
+
const owner = ownerAtEndOfRun(value.slice(runStart, match.index));
|
|
3171
|
+
if (owner !== null)
|
|
3172
|
+
owners.push(owner);
|
|
3173
|
+
}
|
|
3174
|
+
return owners;
|
|
3175
|
+
}
|
|
3176
|
+
function ownerAtEndOfRun(run) {
|
|
3177
|
+
const segments = run.split(".");
|
|
3178
|
+
const last = segments.length - 1;
|
|
3179
|
+
const startsWithLetter = (segment) => /^[a-z]/i.test(segment);
|
|
3180
|
+
if (last < 1 || !startsWithLetter(segments[last]))
|
|
3181
|
+
return null;
|
|
3182
|
+
let first = last;
|
|
3183
|
+
while (first > 0 && startsWithLetter(segments[first - 1]))
|
|
3184
|
+
first--;
|
|
3185
|
+
let offset = 0;
|
|
3186
|
+
for (let i = 0; i < first - 1; i++)
|
|
3187
|
+
offset += segments[i].length + 1;
|
|
3188
|
+
if (first > 0) {
|
|
3189
|
+
const letterAt = segments[first - 1].search(/[a-z]/i);
|
|
3190
|
+
if (letterAt >= 0)
|
|
3191
|
+
return run.slice(offset + letterAt);
|
|
3192
|
+
offset += segments[first - 1].length + 1;
|
|
3193
|
+
}
|
|
3194
|
+
return first < last ? run.slice(offset) : null;
|
|
3195
|
+
}
|
|
3306
3196
|
function containsForeignAppPackage(value, targetAppId) {
|
|
3307
|
-
for (const
|
|
3308
|
-
const owner = match[1];
|
|
3197
|
+
for (const owner of androidResourceOwners(value)) {
|
|
3309
3198
|
if (targetAppId && owner !== targetAppId && !owner.startsWith(`${targetAppId}.`))
|
|
3310
3199
|
return true;
|
|
3311
3200
|
}
|
|
@@ -3442,7 +3331,7 @@ function healMobileTestSteps(input) {
|
|
|
3442
3331
|
summary: changed ? uniq(summary).slice(0, 12) : []
|
|
3443
3332
|
};
|
|
3444
3333
|
}
|
|
3445
|
-
var MOBILE_STEP_ACTIONS, FOREIGN_PACKAGE_RE,
|
|
3334
|
+
var MOBILE_STEP_ACTIONS, FOREIGN_PACKAGE_RE, ANDROID_RESOURCE_ID_SUFFIX_RE, COMMON_EXTERNAL_PREFIXES;
|
|
3446
3335
|
var init_mobile_step_healer = __esm({
|
|
3447
3336
|
"../shared/dist/mobile-step-healer.js"() {
|
|
3448
3337
|
"use strict";
|
|
@@ -3463,7 +3352,7 @@ var init_mobile_step_healer = __esm({
|
|
|
3463
3352
|
"assertStateDelta"
|
|
3464
3353
|
]);
|
|
3465
3354
|
FOREIGN_PACKAGE_RE = /\b[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*){2,}\b/gi;
|
|
3466
|
-
|
|
3355
|
+
ANDROID_RESOURCE_ID_SUFFIX_RE = /:id\//gi;
|
|
3467
3356
|
COMMON_EXTERNAL_PREFIXES = [
|
|
3468
3357
|
"com.android.",
|
|
3469
3358
|
"com.google.",
|
|
@@ -4499,7 +4388,7 @@ function unescapeQuotedBody(raw) {
|
|
|
4499
4388
|
return raw.replace(/\\(['"`\\])/g, "$1");
|
|
4500
4389
|
}
|
|
4501
4390
|
function extractRoleNameValue(line) {
|
|
4502
|
-
const m = line.match(new RegExp(`getByRole\\s*\\([^,]
|
|
4391
|
+
const m = line.match(new RegExp(`getByRole\\s*\\([^,]{1,200},\\s*\\{[^}]{0,200}?name\\s*:\\s*(?:${QUOTED_BODY}|\\/([^/]*?)\\/)`, "i"));
|
|
4503
4392
|
if (!m)
|
|
4504
4393
|
return null;
|
|
4505
4394
|
const raw = m[1] ?? m[2] ?? m[3];
|
|
@@ -4896,7 +4785,7 @@ var init_assertion_classifier = __esm({
|
|
|
4896
4785
|
// toHaveCount(0) stays tier 4 (absence proof) via TIER4_PATTERNS.
|
|
4897
4786
|
[/\.toHaveCount\s*\(\s*[1-9]\d*\s*\)/, "non-zero toHaveCount asserts a record count that changes between runs"],
|
|
4898
4787
|
// parenthesised counts like "(3)" or "[12]" in text - but not Playwright method args
|
|
4899
|
-
[
|
|
4788
|
+
[/\((?<!\.\w{3,20}\s*\()\s*\d+\s*\)(?!\s*(?:[,;]\s*)?\))/i, "parenthesised count is session-specific"],
|
|
4900
4789
|
// bare email addresses in assertion content
|
|
4901
4790
|
[/\b[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}\b/i, "email address is user-specific"],
|
|
4902
4791
|
// currency/balance amounts (dynamic account data)
|
|
@@ -5142,7 +5031,7 @@ var init_assertion_proof = __esm({
|
|
|
5142
5031
|
PRESENCE_MATCHER = /\.(?:toBeVisible|toBeAttached|toBeEnabled|toBeInViewport|toBeEditable|toBeFocused)\s*\(|\.toHaveCount\s*\(\s*1\s*\)/;
|
|
5143
5032
|
TEXT_MATCHER = /\.(?:toHaveText|toContainText)\s*\(/;
|
|
5144
5033
|
ANY_VALUE_SOURCE = /^(?:\.|\\S|\\w|\\d|\[\^\\s\]|\[0-9\]|\[\\d\]|\[\\w\]|\[\\s\\S\])[+*]?\??$/;
|
|
5145
|
-
ANY_VALUE_MATCHER = /\.(?:toHaveValue|toHaveText|toContainText)\s*\(\s*\/((?:\\.|[^/\\]){1,300})\/[a-z]*\s
|
|
5034
|
+
ANY_VALUE_MATCHER = /\.(?:toHaveValue|toHaveText|toContainText)\s*\(\s*\/((?:\\.|[^/\\]){1,300})\/[a-z]*\s*[,)]/;
|
|
5146
5035
|
EMPTY_NOT_VALUE = /\.not\s*\.\s*toHaveValue\s*\(\s*(?:['"`]{2}|\/\^?\$?\/)/;
|
|
5147
5036
|
TEXT_MATCHER_LITERAL = /\.(?:toHaveText|toContainText)\s*\(\s*(['"`])((?:(?!\1).){1,300})\1/;
|
|
5148
5037
|
GET_BY_TEXT_LITERAL = /getByText\s*\(\s*(['"`])((?:(?!\1).){1,300})\1/;
|
|
@@ -5159,7 +5048,7 @@ var init_assertion_proof = __esm({
|
|
|
5159
5048
|
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;
|
|
5160
5049
|
TEXT_LOCATOR = /\.getByText\s*\(|\.filter\s*\(\s*\{\s*hasText\s*:/g;
|
|
5161
5050
|
CSS_LOCATOR = /\.locator\s*\(\s*(['"`])((?:(?!\1).){1,300})\1/g;
|
|
5162
|
-
SPECIFIC_CSS = /\[[^\]]*=|#[\w-]|has-text|:text/;
|
|
5051
|
+
SPECIFIC_CSS = /\[[^\][=]*=|#[\w-]|has-text|:text/;
|
|
5163
5052
|
}
|
|
5164
5053
|
});
|
|
5165
5054
|
|
|
@@ -5207,6 +5096,8 @@ function stripCoverageComments(code) {
|
|
|
5207
5096
|
let copyFrom = 0;
|
|
5208
5097
|
let i = 0;
|
|
5209
5098
|
let quote = null;
|
|
5099
|
+
let prev = "";
|
|
5100
|
+
let noRegexBefore = 0;
|
|
5210
5101
|
while (i < code.length) {
|
|
5211
5102
|
const ch = code[i];
|
|
5212
5103
|
if (quote) {
|
|
@@ -5214,12 +5105,24 @@ function stripCoverageComments(code) {
|
|
|
5214
5105
|
i += 2;
|
|
5215
5106
|
continue;
|
|
5216
5107
|
}
|
|
5217
|
-
if (ch === quote || ch === "\n" && quote !== "`")
|
|
5108
|
+
if (ch === quote || ch === "\n" && quote !== "`") {
|
|
5218
5109
|
quote = null;
|
|
5110
|
+
prev = ch === "\n" ? prev : ch;
|
|
5111
|
+
}
|
|
5219
5112
|
i++;
|
|
5220
5113
|
continue;
|
|
5221
5114
|
}
|
|
5222
5115
|
const next = code[i + 1];
|
|
5116
|
+
if (ch === "/" && next !== "*" && next !== "/" && i >= noRegexBefore && (prev === "" || REGEX_PRECEDER2.includes(prev))) {
|
|
5117
|
+
const end = regexLiteralEnd3(code, i);
|
|
5118
|
+
if (end > 0) {
|
|
5119
|
+
i = end;
|
|
5120
|
+
prev = "a";
|
|
5121
|
+
continue;
|
|
5122
|
+
}
|
|
5123
|
+
const eol = code.indexOf("\n", i);
|
|
5124
|
+
noRegexBefore = eol === -1 ? code.length : eol;
|
|
5125
|
+
}
|
|
5223
5126
|
if (ch === "/" && next === "*") {
|
|
5224
5127
|
const end = code.indexOf("*/", i + 2);
|
|
5225
5128
|
const stop = end === -1 ? code.length : end + 2;
|
|
@@ -5238,11 +5141,39 @@ function stripCoverageComments(code) {
|
|
|
5238
5141
|
}
|
|
5239
5142
|
if (ch === '"' || ch === "'" || ch === "`")
|
|
5240
5143
|
quote = ch;
|
|
5144
|
+
if (ch !== " " && ch !== " " && ch !== "\n" && ch !== "\r")
|
|
5145
|
+
prev = ch;
|
|
5241
5146
|
i++;
|
|
5242
5147
|
}
|
|
5243
5148
|
out.push(code.slice(copyFrom));
|
|
5244
5149
|
return out.join("");
|
|
5245
5150
|
}
|
|
5151
|
+
function regexLiteralEnd3(code, start) {
|
|
5152
|
+
let inClass = false;
|
|
5153
|
+
for (let j = start + 1; j < code.length; j++) {
|
|
5154
|
+
const c = code[j];
|
|
5155
|
+
if (c === "\n")
|
|
5156
|
+
return -1;
|
|
5157
|
+
if (c === "\\") {
|
|
5158
|
+
j++;
|
|
5159
|
+
continue;
|
|
5160
|
+
}
|
|
5161
|
+
if (inClass) {
|
|
5162
|
+
if (c === "]")
|
|
5163
|
+
inClass = false;
|
|
5164
|
+
continue;
|
|
5165
|
+
}
|
|
5166
|
+
if (c === "[")
|
|
5167
|
+
inClass = true;
|
|
5168
|
+
else if (c === "/") {
|
|
5169
|
+
let k = j + 1;
|
|
5170
|
+
while (k < code.length && isIdentChar(code[k]))
|
|
5171
|
+
k++;
|
|
5172
|
+
return k;
|
|
5173
|
+
}
|
|
5174
|
+
}
|
|
5175
|
+
return -1;
|
|
5176
|
+
}
|
|
5246
5177
|
function isIdentChar(ch) {
|
|
5247
5178
|
if (!ch)
|
|
5248
5179
|
return false;
|
|
@@ -5900,7 +5831,7 @@ function assessTitleCoverage(title, code) {
|
|
|
5900
5831
|
const repeatedHit = [...repeated].some((term) => hay.includes(term));
|
|
5901
5832
|
return { covered: hit >= needed || repeatedHit, missingTerms };
|
|
5902
5833
|
}
|
|
5903
|
-
var COVERAGE_STOP_WORDS, MAX_ASSERTION_JOIN_CHARS, MAX_COVERAGE_LINE_CHARS, BLOCK_SCAFFOLD, cachedAssertionInput, cachedAssertionSource, LOCAL_BINDING, ASSERTED_STATUS_RE, MAX_RESOLVED_NAMES_PER_LINE, WORD_CHAR_RE, NOT_CONTAIN_TEXT_RE, PLACE_PHRASE_RE, PLACE_WORD_MIN_CHARS, PLACE_FUNCTION_WORDS, HIDDEN_STATE_TERMS, HIDDEN_ELEMENT_LINE_RE, HIDDEN_LINE_RE, MAX_SHOWN_EARLIER_LINES, SHOWN_EARLIER_LITERAL_RE, SHARED_PREFIX_MIN_CHARS, MAX_SHOWN_EARLIER_LITERALS, VALUE_SHAPE_RE, VALUE_SHAPE_ALL_RE, TITLE_TERM_MIN;
|
|
5834
|
+
var COVERAGE_STOP_WORDS, MAX_ASSERTION_JOIN_CHARS, MAX_COVERAGE_LINE_CHARS, REGEX_PRECEDER2, BLOCK_SCAFFOLD, cachedAssertionInput, cachedAssertionSource, LOCAL_BINDING, ASSERTED_STATUS_RE, MAX_RESOLVED_NAMES_PER_LINE, WORD_CHAR_RE, NOT_CONTAIN_TEXT_RE, PLACE_PHRASE_RE, PLACE_WORD_MIN_CHARS, PLACE_FUNCTION_WORDS, HIDDEN_STATE_TERMS, HIDDEN_ELEMENT_LINE_RE, HIDDEN_LINE_RE, MAX_SHOWN_EARLIER_LINES, SHOWN_EARLIER_LITERAL_RE, SHARED_PREFIX_MIN_CHARS, MAX_SHOWN_EARLIER_LITERALS, VALUE_SHAPE_RE, VALUE_SHAPE_ALL_RE, TITLE_TERM_MIN;
|
|
5904
5835
|
var init_expected_outcome_coverage = __esm({
|
|
5905
5836
|
"../shared/dist/expected-outcome-coverage.js"() {
|
|
5906
5837
|
"use strict";
|
|
@@ -5937,6 +5868,7 @@ var init_expected_outcome_coverage = __esm({
|
|
|
5937
5868
|
]);
|
|
5938
5869
|
MAX_ASSERTION_JOIN_CHARS = 8e3;
|
|
5939
5870
|
MAX_COVERAGE_LINE_CHARS = 2e3;
|
|
5871
|
+
REGEX_PRECEDER2 = "(,=:[!&|?{};+-*%<>~^";
|
|
5940
5872
|
BLOCK_SCAFFOLD = /=>|\b(?:test|describe|it|beforeEach|afterEach|beforeAll|afterAll|function)\b/;
|
|
5941
5873
|
cachedAssertionInput = null;
|
|
5942
5874
|
cachedAssertionSource = { raw: "", normalized: "", bindings: /* @__PURE__ */ new Map(), bindingRefs: /* @__PURE__ */ new Map() };
|
|
@@ -6080,22 +6012,37 @@ function classifyAuthHandoffIntent(text) {
|
|
|
6080
6012
|
function withoutIdpUrlAsserts(code) {
|
|
6081
6013
|
return code.replace(/\.(?:toHaveURL|waitForURL)\s*\((?:[^)(]|\([^)]*\))*\)/g, (match) => /accountchooser|accounts\.google|appleid\.apple|login\.microsoftonline|\/v\d+\/signin|oauth/i.test(match) ? "" : match);
|
|
6082
6014
|
}
|
|
6015
|
+
function stripLineComment(line) {
|
|
6016
|
+
let from = line.length;
|
|
6017
|
+
while (from > 0) {
|
|
6018
|
+
const c = line.charCodeAt(from - 1);
|
|
6019
|
+
if (c === 10 || c === 13 || c === 8232 || c === 8233)
|
|
6020
|
+
break;
|
|
6021
|
+
from--;
|
|
6022
|
+
}
|
|
6023
|
+
const at = line.indexOf("//", from);
|
|
6024
|
+
return at < 0 ? line : line.slice(0, at);
|
|
6025
|
+
}
|
|
6083
6026
|
function stripComments(code) {
|
|
6084
|
-
return replaceBlockComments(code, () => "").split("\n").map(
|
|
6027
|
+
return replaceBlockComments(code, () => "").split("\n").map(stripLineComment).join("\n");
|
|
6085
6028
|
}
|
|
6086
|
-
function
|
|
6087
|
-
const
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
let depth = 0;
|
|
6091
|
-
for (let i = open; i < code.length; i++) {
|
|
6029
|
+
function closingParens(code) {
|
|
6030
|
+
const close = new Int32Array(code.length).fill(-1);
|
|
6031
|
+
const open = [];
|
|
6032
|
+
for (let i = 0; i < code.length; i++) {
|
|
6092
6033
|
const c = code[i];
|
|
6093
6034
|
if (c === "(")
|
|
6094
|
-
|
|
6095
|
-
else if (c === ")" &&
|
|
6096
|
-
|
|
6035
|
+
open.push(i);
|
|
6036
|
+
else if (c === ")" && open.length > 0)
|
|
6037
|
+
close[open.pop()] = i;
|
|
6097
6038
|
}
|
|
6098
|
-
return
|
|
6039
|
+
return close;
|
|
6040
|
+
}
|
|
6041
|
+
function balancedCall(code, close, start) {
|
|
6042
|
+
const open = code.indexOf("(", start);
|
|
6043
|
+
if (open < 0 || close[open] < 0)
|
|
6044
|
+
return null;
|
|
6045
|
+
return { inner: code.slice(open + 1, close[open]), end: close[open] };
|
|
6099
6046
|
}
|
|
6100
6047
|
function resolveLocator(code, subject) {
|
|
6101
6048
|
const id = subject.trim().match(/^([A-Za-z_$][\w$]*)$/);
|
|
@@ -6143,8 +6090,10 @@ function isAuthHandoffDismissCode(code) {
|
|
|
6143
6090
|
const expects = [];
|
|
6144
6091
|
const re = new RegExp(EXPECT_CALL_RE.source, "g");
|
|
6145
6092
|
let match;
|
|
6093
|
+
let close = null;
|
|
6146
6094
|
while (match = re.exec(body)) {
|
|
6147
|
-
|
|
6095
|
+
close ??= closingParens(body);
|
|
6096
|
+
const found = balancedCall(body, close, match.index);
|
|
6148
6097
|
if (found)
|
|
6149
6098
|
expects.push({ inner: found.inner, tail: body.slice(found.end, found.end + 48) });
|
|
6150
6099
|
}
|
|
@@ -6189,35 +6138,232 @@ var init_auth_handoff_coverage = __esm({
|
|
|
6189
6138
|
}
|
|
6190
6139
|
});
|
|
6191
6140
|
|
|
6192
|
-
// ../shared/dist/
|
|
6193
|
-
function
|
|
6194
|
-
|
|
6195
|
-
|
|
6141
|
+
// ../shared/dist/auth-plan.js
|
|
6142
|
+
function hasInlineLoginSteps(playwrightCode) {
|
|
6143
|
+
if (!playwrightCode)
|
|
6144
|
+
return false;
|
|
6145
|
+
return /process\.env\.TEST_USER_EMAIL!?/.test(playwrightCode) && /process\.env\.TEST_USER_PASSWORD!?/.test(playwrightCode);
|
|
6196
6146
|
}
|
|
6197
|
-
function
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
if (quote) {
|
|
6202
|
-
if (c === quote && ln[i - 1] !== "\\")
|
|
6203
|
-
quote = null;
|
|
6204
|
-
continue;
|
|
6205
|
-
}
|
|
6206
|
-
if (c === '"' || c === "'" || c === "`") {
|
|
6207
|
-
quote = c;
|
|
6208
|
-
continue;
|
|
6209
|
-
}
|
|
6210
|
-
if (c === "/" && ln[i + 1] === "/")
|
|
6211
|
-
return ln.slice(0, i);
|
|
6212
|
-
}
|
|
6213
|
-
return ln;
|
|
6147
|
+
function hasGeneratedAuthGuard(playwrightCode) {
|
|
6148
|
+
if (!playwrightCode)
|
|
6149
|
+
return false;
|
|
6150
|
+
return /\/\/ >>> VALIDATE_QA_AUTH v(\d+)\b[\s\S]*?process\.env\.AUTH_FILE[\s\S]*?\/\/ <<< VALIDATE_QA_AUTH v\1\b/.test(playwrightCode);
|
|
6214
6151
|
}
|
|
6215
|
-
function
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6152
|
+
function legacyNeedsCookieState(tags, code, sharedAuthInjectEnabled) {
|
|
6153
|
+
if (!sharedAuthInjectEnabled)
|
|
6154
|
+
return false;
|
|
6155
|
+
if (tags.includes("@auth"))
|
|
6156
|
+
return false;
|
|
6157
|
+
if (tags.includes("@api"))
|
|
6158
|
+
return true;
|
|
6159
|
+
if (!tags.includes("@needs-auth"))
|
|
6160
|
+
return false;
|
|
6161
|
+
if (sharedAuthInjectEnabled && tags.includes("@auth-guarded") && hasGeneratedAuthGuard(code))
|
|
6162
|
+
return true;
|
|
6163
|
+
const isGeneratedVariant = tags.includes("@edge") || tags.includes("@bug");
|
|
6164
|
+
const hasLegacyInlineLogin = tags.includes("@discovered") && !isGeneratedVariant && hasInlineLoginSteps(code);
|
|
6165
|
+
return !tags.includes("@inline-login") && !hasLegacyInlineLogin;
|
|
6166
|
+
}
|
|
6167
|
+
function codeReadsStoredCredentials(code) {
|
|
6168
|
+
if (!code)
|
|
6169
|
+
return false;
|
|
6170
|
+
if (/__vqaLogin\b/.test(code))
|
|
6171
|
+
return true;
|
|
6172
|
+
for (const m of code.matchAll(/TEST_[A-Z0-9_]+/g)) {
|
|
6173
|
+
if (/_(?:EMAIL|PASSWORD)/.test(m[0]))
|
|
6174
|
+
return true;
|
|
6175
|
+
}
|
|
6176
|
+
for (const m of code.matchAll(/process\.env\s*\[([^\]]*)\]?/g)) {
|
|
6177
|
+
if (!/^\s*(['"])[^'"`]*\1\s*$/.test(m[1]))
|
|
6178
|
+
return true;
|
|
6179
|
+
}
|
|
6180
|
+
return false;
|
|
6181
|
+
}
|
|
6182
|
+
function explicitAuthTypeTags(t) {
|
|
6183
|
+
const found = /* @__PURE__ */ new Set();
|
|
6184
|
+
for (const tag of t) {
|
|
6185
|
+
if (AUTH_TYPE_TAGS.includes(tag))
|
|
6186
|
+
found.add(tag);
|
|
6187
|
+
else if (Object.hasOwn(AUTH_TAG_ALIASES, tag))
|
|
6188
|
+
found.add(AUTH_TAG_ALIASES[tag]);
|
|
6189
|
+
}
|
|
6190
|
+
return [...found];
|
|
6191
|
+
}
|
|
6192
|
+
function invalidPlan(configurationError) {
|
|
6193
|
+
return {
|
|
6194
|
+
version: 1,
|
|
6195
|
+
source: "invalid",
|
|
6196
|
+
acquisition: "unsupported",
|
|
6197
|
+
presentation: "none",
|
|
6198
|
+
needsCookieState: false,
|
|
6199
|
+
needsStandingHeaders: false,
|
|
6200
|
+
standingHeadersMode: "none",
|
|
6201
|
+
needsLoginCreds: false,
|
|
6202
|
+
requiredEnvKeys: [],
|
|
6203
|
+
configurationError
|
|
6204
|
+
};
|
|
6205
|
+
}
|
|
6206
|
+
function explicitPlan(authTypeTag2, acquisition, presentation, opts) {
|
|
6207
|
+
return {
|
|
6208
|
+
version: 1,
|
|
6209
|
+
source: "explicit",
|
|
6210
|
+
authTypeTag: authTypeTag2,
|
|
6211
|
+
acquisition,
|
|
6212
|
+
presentation,
|
|
6213
|
+
needsCookieState: opts.cookie,
|
|
6214
|
+
needsStandingHeaders: opts.standingMode === "required",
|
|
6215
|
+
standingHeadersMode: opts.standingMode,
|
|
6216
|
+
needsLoginCreds: TEST_USER_ENV_KEYS.every((k) => opts.requiredEnvKeys.includes(k)),
|
|
6217
|
+
requiredEnvKeys: opts.requiredEnvKeys
|
|
6218
|
+
};
|
|
6219
|
+
}
|
|
6220
|
+
function deriveAuthPlan(tags, code, opts) {
|
|
6221
|
+
const t = Array.isArray(tags) ? tags : [];
|
|
6222
|
+
const sharedFlag = opts?.sharedAuthInjectEnabled ?? false;
|
|
6223
|
+
const isApi = t.includes("@api");
|
|
6224
|
+
const explicit = isApi ? explicitAuthTypeTags(t) : [];
|
|
6225
|
+
if (explicit.length > 0) {
|
|
6226
|
+
if (t.includes("@auth")) {
|
|
6227
|
+
return invalidPlan("@auth (self-login UI) cannot be combined with an explicit @api auth-type tag");
|
|
6228
|
+
}
|
|
6229
|
+
const loginCompose = explicit.filter((e) => e === AUTH_TAG_COOKIE_STATE || e === AUTH_TAG_LOGIN_COOKIE || e === AUTH_TAG_LOGIN_BEARER);
|
|
6230
|
+
if (explicit.length === 2 && explicit.includes(AUTH_TAG_STANDING) && loginCompose.length === 1) {
|
|
6231
|
+
const composedKeys = [...TEST_USER_ENV_KEYS, API_AUTH_HEADERS_ENV_KEY];
|
|
6232
|
+
switch (loginCompose[0]) {
|
|
6233
|
+
case AUTH_TAG_COOKIE_STATE:
|
|
6234
|
+
return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
6235
|
+
case AUTH_TAG_LOGIN_COOKIE:
|
|
6236
|
+
return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
6237
|
+
case AUTH_TAG_LOGIN_BEARER:
|
|
6238
|
+
return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
6239
|
+
}
|
|
6240
|
+
}
|
|
6241
|
+
if (explicit.length > 1) {
|
|
6242
|
+
return invalidPlan(`conflicting auth-type tags: ${explicit.join(", ")}`);
|
|
6243
|
+
}
|
|
6244
|
+
switch (explicit[0]) {
|
|
6245
|
+
case AUTH_TAG_UNSUPPORTED:
|
|
6246
|
+
return {
|
|
6247
|
+
...invalidPlan("auth scheme not yet drivable (e.g. HMAC / mTLS / provider-replay) \u2014 needs manual configuration")
|
|
6248
|
+
};
|
|
6249
|
+
case AUTH_TAG_NONE:
|
|
6250
|
+
return explicitPlan(AUTH_TAG_NONE, "none", "none", { cookie: false, standingMode: "none", requiredEnvKeys: [] });
|
|
6251
|
+
case AUTH_TAG_STANDING:
|
|
6252
|
+
return explicitPlan(AUTH_TAG_STANDING, "standing-secret", "standing-header", { cookie: false, standingMode: "required", requiredEnvKeys: [API_AUTH_HEADERS_ENV_KEY] });
|
|
6253
|
+
case AUTH_TAG_LOGIN_BEARER:
|
|
6254
|
+
return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "none", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
6255
|
+
case AUTH_TAG_LOGIN_COOKIE:
|
|
6256
|
+
return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
6257
|
+
case AUTH_TAG_COOKIE_STATE:
|
|
6258
|
+
return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
6259
|
+
}
|
|
6260
|
+
}
|
|
6261
|
+
const cookie = legacyNeedsCookieState(t, code, sharedFlag);
|
|
6262
|
+
const authedLegacy = t.includes("@auth") || t.includes("@needs-auth") || isApi;
|
|
6263
|
+
const selfLoginWithoutStoredCreds = t.includes("@auth") && !t.includes("@needs-auth") && !isApi && !!code?.trim() && !/TEST_USER_EMAIL|TEST_USER_PASSWORD|__vqaLogin\b/.test(code);
|
|
6264
|
+
const apiWithoutStoredCreds = isApi && !t.includes("@auth") && !t.includes("@needs-auth") && !!code?.trim() && !codeReadsStoredCredentials(code);
|
|
6265
|
+
const needsLoginCreds = authedLegacy && !selfLoginWithoutStoredCreds && !apiWithoutStoredCreds;
|
|
6266
|
+
return {
|
|
6267
|
+
version: 1,
|
|
6268
|
+
source: "legacy",
|
|
6269
|
+
acquisition: cookie ? "browser-bootstrap" : authedLegacy ? "request-login" : "none",
|
|
6270
|
+
presentation: authedLegacy ? "cookie" : "none",
|
|
6271
|
+
needsCookieState: cookie,
|
|
6272
|
+
needsStandingHeaders: false,
|
|
6273
|
+
// A legacy bare-@api test keeps injecting standing headers IF the project configured them
|
|
6274
|
+
// (preserving today's behavior) — but does not require them. Non-@api legacy tests never inject.
|
|
6275
|
+
standingHeadersMode: isApi ? "legacy-if-configured" : "none",
|
|
6276
|
+
needsLoginCreds,
|
|
6277
|
+
requiredEnvKeys: needsLoginCreds ? [...TEST_USER_ENV_KEYS] : []
|
|
6278
|
+
};
|
|
6279
|
+
}
|
|
6280
|
+
function authTypeTag(tags) {
|
|
6281
|
+
const t = Array.isArray(tags) ? tags : [];
|
|
6282
|
+
const explicit = explicitAuthTypeTags(t);
|
|
6283
|
+
return explicit.length === 1 ? explicit[0] : void 0;
|
|
6284
|
+
}
|
|
6285
|
+
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;
|
|
6286
|
+
var init_auth_plan = __esm({
|
|
6287
|
+
"../shared/dist/auth-plan.js"() {
|
|
6288
|
+
"use strict";
|
|
6289
|
+
AUTH_TAG_COOKIE_STATE = "@auth-cookie-state";
|
|
6290
|
+
AUTH_TAG_LOGIN_COOKIE = "@auth-login-cookie";
|
|
6291
|
+
AUTH_TAG_LOGIN_BEARER = "@auth-login-bearer";
|
|
6292
|
+
AUTH_TAG_STANDING = "@auth-standing";
|
|
6293
|
+
AUTH_TAG_NONE = "@auth-none";
|
|
6294
|
+
AUTH_TAG_UNSUPPORTED = "@auth-unsupported";
|
|
6295
|
+
AUTH_TYPE_TAGS = [
|
|
6296
|
+
AUTH_TAG_COOKIE_STATE,
|
|
6297
|
+
AUTH_TAG_LOGIN_COOKIE,
|
|
6298
|
+
AUTH_TAG_LOGIN_BEARER,
|
|
6299
|
+
AUTH_TAG_STANDING,
|
|
6300
|
+
AUTH_TAG_NONE,
|
|
6301
|
+
AUTH_TAG_UNSUPPORTED
|
|
6302
|
+
];
|
|
6303
|
+
AUTH_TAG_ALIASES = {
|
|
6304
|
+
"@auth-cookie": AUTH_TAG_COOKIE_STATE,
|
|
6305
|
+
"@auth-bearer": AUTH_TAG_LOGIN_BEARER,
|
|
6306
|
+
"@auth-token": AUTH_TAG_LOGIN_BEARER,
|
|
6307
|
+
"@auth-api-key": AUTH_TAG_STANDING,
|
|
6308
|
+
"@auth-standing-header": AUTH_TAG_STANDING
|
|
6309
|
+
};
|
|
6310
|
+
TEST_USER_ENV_KEYS = ["TEST_USER_EMAIL", "TEST_USER_PASSWORD"];
|
|
6311
|
+
API_AUTH_HEADERS_ENV_KEY = "API_AUTH_HEADERS";
|
|
6312
|
+
}
|
|
6313
|
+
});
|
|
6314
|
+
|
|
6315
|
+
// ../shared/dist/assertion-strength.js
|
|
6316
|
+
function parseLastActionProofMode(raw) {
|
|
6317
|
+
const v = (raw ?? "").trim().toLowerCase();
|
|
6318
|
+
return v === "on" || v === "shadow" ? v : "off";
|
|
6319
|
+
}
|
|
6320
|
+
function isUrlProof(stmt) {
|
|
6321
|
+
if (URL_PROOF.test(stmt))
|
|
6322
|
+
return true;
|
|
6323
|
+
let calls = 0;
|
|
6324
|
+
for (const open of stmt.matchAll(POLL_OPEN)) {
|
|
6325
|
+
if (++calls > POLL_CALLS_MAX)
|
|
6326
|
+
break;
|
|
6327
|
+
const from = open.index + open[0].length;
|
|
6328
|
+
const args = balancedArguments(stmt, from, MATCHER_ARGS_MAX);
|
|
6329
|
+
if (args === null)
|
|
6330
|
+
continue;
|
|
6331
|
+
const subject = pollSubject(args);
|
|
6332
|
+
if (!POLL_PAGE_URL.test(subject))
|
|
6333
|
+
continue;
|
|
6334
|
+
if (!POLL_PAGE_URL_ONLY.test(subject) || PAGE_VALUE_MATCHER.test(stmt.slice(from + args.length)))
|
|
6335
|
+
return true;
|
|
6336
|
+
}
|
|
6337
|
+
return false;
|
|
6338
|
+
}
|
|
6339
|
+
function liveExpectStatements(uncommented) {
|
|
6340
|
+
const masked = maskedOutsideStrings(uncommented).split("\n");
|
|
6341
|
+
return accumulateStatements(uncommented.split("\n")).filter((s) => EXPECT_STMT.test(s.stmt) && EXPECT_STMT.test(masked[s.startLine - 1] ?? ""));
|
|
6342
|
+
}
|
|
6343
|
+
function stripLineComment2(ln) {
|
|
6344
|
+
let quote = null;
|
|
6345
|
+
for (let i = 0; i < ln.length; i++) {
|
|
6346
|
+
const c = ln[i];
|
|
6347
|
+
if (quote) {
|
|
6348
|
+
if (c === quote && ln[i - 1] !== "\\")
|
|
6349
|
+
quote = null;
|
|
6350
|
+
continue;
|
|
6351
|
+
}
|
|
6352
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
6353
|
+
quote = c;
|
|
6354
|
+
continue;
|
|
6355
|
+
}
|
|
6356
|
+
if (c === "/" && ln[i + 1] === "/")
|
|
6357
|
+
return ln.slice(0, i);
|
|
6358
|
+
}
|
|
6359
|
+
return ln;
|
|
6360
|
+
}
|
|
6361
|
+
function splitInteractionStatementsWithLines(lines) {
|
|
6362
|
+
const out = [];
|
|
6363
|
+
let depth = 0;
|
|
6364
|
+
let scaffold = false;
|
|
6365
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6366
|
+
const ln = stripLineComment2(lines[i]).trim();
|
|
6221
6367
|
if (!ln)
|
|
6222
6368
|
continue;
|
|
6223
6369
|
const isContinuation = out.length > 0 && (ln.startsWith(".") || depth > 0 && !scaffold);
|
|
@@ -6241,7 +6387,7 @@ function testBlockIds(lines) {
|
|
|
6241
6387
|
let any = false;
|
|
6242
6388
|
let block = 0;
|
|
6243
6389
|
for (let i = 0; i < lines.length; i++) {
|
|
6244
|
-
const ln =
|
|
6390
|
+
const ln = stripLineComment2(lines[i]).trim();
|
|
6245
6391
|
if (!open) {
|
|
6246
6392
|
if (!TEST_BLOCK_OPEN.test(ln))
|
|
6247
6393
|
continue;
|
|
@@ -6325,7 +6471,7 @@ function expectSubjectSpan(stmt) {
|
|
|
6325
6471
|
}
|
|
6326
6472
|
return null;
|
|
6327
6473
|
}
|
|
6328
|
-
function resolveSubjectBindings(stmts, bindings, blockOfLine) {
|
|
6474
|
+
function resolveSubjectBindings(stmts, bindings, blockOfLine, boundAt) {
|
|
6329
6475
|
if (bindings.length === 0)
|
|
6330
6476
|
return [...stmts];
|
|
6331
6477
|
return stmts.map((item) => {
|
|
@@ -6348,14 +6494,22 @@ function resolveSubjectBindings(stmts, bindings, blockOfLine) {
|
|
|
6348
6494
|
subject = `${lead[1]}${found.expr.replace(/;\s*$/, "")}${subject.slice(lead[0].length)}`;
|
|
6349
6495
|
line = found.line;
|
|
6350
6496
|
}
|
|
6351
|
-
|
|
6497
|
+
const out = { ...item, stmt: `${item.stmt.slice(0, span.from)}${subject}${item.stmt.slice(span.to)}` };
|
|
6498
|
+
if (boundAt !== void 0 && line !== item.startLine)
|
|
6499
|
+
boundAt.set(out, line);
|
|
6500
|
+
return out;
|
|
6352
6501
|
});
|
|
6353
6502
|
}
|
|
6354
6503
|
function stripLoginScaffolding(code) {
|
|
6355
|
-
const
|
|
6504
|
+
const lines = code.split("\n");
|
|
6505
|
+
return keptScaffoldingLines(lines).map((i) => lines[i]).join("\n");
|
|
6506
|
+
}
|
|
6507
|
+
function keptScaffoldingLines(lines) {
|
|
6508
|
+
const kept = [];
|
|
6356
6509
|
let inAuth = false;
|
|
6357
6510
|
let inInbox = false;
|
|
6358
|
-
for (
|
|
6511
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6512
|
+
const ln = lines[i];
|
|
6359
6513
|
if (!inAuth && !inInbox && AUTH_START.test(ln)) {
|
|
6360
6514
|
inAuth = true;
|
|
6361
6515
|
continue;
|
|
@@ -6376,9 +6530,9 @@ function stripLoginScaffolding(code) {
|
|
|
6376
6530
|
}
|
|
6377
6531
|
if (LOGIN_CALL_LINE.test(ln))
|
|
6378
6532
|
continue;
|
|
6379
|
-
|
|
6533
|
+
kept.push(i);
|
|
6380
6534
|
}
|
|
6381
|
-
return
|
|
6535
|
+
return kept;
|
|
6382
6536
|
}
|
|
6383
6537
|
function stripCommentsAndLogs(code) {
|
|
6384
6538
|
return stripCoverageComments(code).split("\n").filter((ln) => !/^\s*console\./.test(ln)).join("\n");
|
|
@@ -6387,7 +6541,7 @@ function detectsErrorBannerAssertion(code) {
|
|
|
6387
6541
|
const cleaned = stripCommentsAndLogs(stripLoginScaffolding(code));
|
|
6388
6542
|
const lines = cleaned.split("\n");
|
|
6389
6543
|
const candidates = [];
|
|
6390
|
-
const positive = (stmt) => !
|
|
6544
|
+
const positive = (stmt) => !claimsAbsence(stmt);
|
|
6391
6545
|
const statements = liveExpectStatements(cleaned).map((s) => s.stmt);
|
|
6392
6546
|
for (const stmt of statements) {
|
|
6393
6547
|
if (positive(stmt))
|
|
@@ -6516,6 +6670,12 @@ function detectsObservedFailureAsExpected(code) {
|
|
|
6516
6670
|
return FAILURE_STATUS_ASSERT.some((re) => re.test(stmt)) || hasFailedListener && FAILED_LIST_ASSERT.test(stmt);
|
|
6517
6671
|
});
|
|
6518
6672
|
}
|
|
6673
|
+
function claimsAbsence(stmt) {
|
|
6674
|
+
const negated = NEGATED_ASSERT_NOT.test(stmt);
|
|
6675
|
+
const bare = negated ? stmt.replace(NOT_BEFORE_MATCHER, ".") : stmt;
|
|
6676
|
+
const absenceMatcher = /\.toBeHidden\s*\(|\.toHaveCount\s*\(\s*0\s*\)/.test(bare) || VALUE_ABSENCE.test(bare) || OPTION_ABSENCE.test(bare);
|
|
6677
|
+
return negated !== absenceMatcher;
|
|
6678
|
+
}
|
|
6519
6679
|
function isVacuousErrorAbsence(stmt, presence) {
|
|
6520
6680
|
if (!ABSENCE_MATCHER.test(stmt))
|
|
6521
6681
|
return false;
|
|
@@ -6524,6 +6684,41 @@ function isVacuousErrorAbsence(stmt, presence) {
|
|
|
6524
6684
|
return false;
|
|
6525
6685
|
return !presence().includes(m[1]);
|
|
6526
6686
|
}
|
|
6687
|
+
function isVacuousAccountAbsence(stmt, shown, account) {
|
|
6688
|
+
if (!claimsAbsence(stmt))
|
|
6689
|
+
return false;
|
|
6690
|
+
if (!ABSENCE_MATCHER.test(stmt)) {
|
|
6691
|
+
const negated = NEGATED_TEXT.exec(stmt);
|
|
6692
|
+
if (!negated)
|
|
6693
|
+
return false;
|
|
6694
|
+
const read = account.exec(stmt.slice(negated.index));
|
|
6695
|
+
return read !== null && !shown().has(read[0]);
|
|
6696
|
+
}
|
|
6697
|
+
const m = account.exec(stmt);
|
|
6698
|
+
if (!m || shown().has(m[0]))
|
|
6699
|
+
return false;
|
|
6700
|
+
for (let at = endOfEnclosingCall(stmt, m.index + m[0].length); at < stmt.length; at = endOfEnclosingCall(stmt, at)) {
|
|
6701
|
+
SCOPING_STEP.lastIndex = at;
|
|
6702
|
+
if (SCOPING_STEP.test(stmt))
|
|
6703
|
+
return false;
|
|
6704
|
+
}
|
|
6705
|
+
return true;
|
|
6706
|
+
}
|
|
6707
|
+
function endOfEnclosingCall(stmt, from) {
|
|
6708
|
+
let depth = 0;
|
|
6709
|
+
for (let i = from; i < stmt.length; i++) {
|
|
6710
|
+
const c = stmt[i];
|
|
6711
|
+
if (c === "'" || c === '"') {
|
|
6712
|
+
for (i++; i < stmt.length && stmt[i] !== c; i++)
|
|
6713
|
+
if (stmt[i] === "\\")
|
|
6714
|
+
i++;
|
|
6715
|
+
} else if (c === "(")
|
|
6716
|
+
depth++;
|
|
6717
|
+
else if (c === ")" && depth-- === 0)
|
|
6718
|
+
return i + 1;
|
|
6719
|
+
}
|
|
6720
|
+
return stmt.length;
|
|
6721
|
+
}
|
|
6527
6722
|
function isBareContainerVisibility(stmt) {
|
|
6528
6723
|
return BARE_CONTAINER_ROLE.test(stmt) && !GET_BY_TEXT.test(stmt) && !HAS_TEXT_FILTER.test(stmt) && !TEXT_CONTENT_MATCHER.test(stmt);
|
|
6529
6724
|
}
|
|
@@ -6531,19 +6726,53 @@ function isChromeTarget(stmt) {
|
|
|
6531
6726
|
return CHROME_LOCATOR.test(stmt) && !CONTENT_ROLE.test(stmt) && !/getByText\s*\(/.test(stmt);
|
|
6532
6727
|
}
|
|
6533
6728
|
function countsAsContentText(stmt) {
|
|
6534
|
-
if (!TEXT_CONTENT_MATCHER.test(stmt))
|
|
6729
|
+
if (!TEXT_CONTENT_MATCHER.test(stmt) || NEGATED_ASSERT.test(stmt))
|
|
6535
6730
|
return false;
|
|
6536
6731
|
if (isSymbolOnlyArgument(stmt, TEXT_CONTENT_MATCHER))
|
|
6537
6732
|
return false;
|
|
6538
|
-
|
|
6733
|
+
return onContentTarget(stmt);
|
|
6734
|
+
}
|
|
6735
|
+
function onContentTarget(stmt) {
|
|
6736
|
+
return !isChromeTarget(stmt) || /getByRole\s*\(\s*['"`]main['"`]/i.test(stmt);
|
|
6737
|
+
}
|
|
6738
|
+
function provesTextRemoved(stmt, presence) {
|
|
6739
|
+
const m = stmt.match(NEGATED_TEXT);
|
|
6740
|
+
if (!m || !onContentTarget(stmt))
|
|
6741
|
+
return false;
|
|
6742
|
+
const text = m[1] ?? m[2];
|
|
6743
|
+
if (text === void 0)
|
|
6539
6744
|
return true;
|
|
6540
|
-
|
|
6745
|
+
if (!/[\p{L}\p{N}]/u.test(text))
|
|
6746
|
+
return false;
|
|
6747
|
+
return !ERROR_TEXT_WORDS.test(text) || presence().includes(text);
|
|
6541
6748
|
}
|
|
6542
6749
|
function isDataContentRole(stmt) {
|
|
6543
6750
|
return CONTENT_ROLE.test(stmt) && !ANNOUNCE_ROLE.test(stmt);
|
|
6544
6751
|
}
|
|
6752
|
+
function destructuresRequest(code) {
|
|
6753
|
+
let readTo = -1;
|
|
6754
|
+
for (const m of code.matchAll(ASYNC_DESTRUCTURE_OPEN)) {
|
|
6755
|
+
const start = m.index + m[0].length;
|
|
6756
|
+
if (start <= readTo)
|
|
6757
|
+
continue;
|
|
6758
|
+
let end = start;
|
|
6759
|
+
while (end < code.length && code[end] !== "}" && code[end] !== ")")
|
|
6760
|
+
end++;
|
|
6761
|
+
readTo = end;
|
|
6762
|
+
if (end === code.length)
|
|
6763
|
+
return false;
|
|
6764
|
+
if (code[end] !== "}" || !REQUEST_WORD.test(code.slice(start, end)))
|
|
6765
|
+
continue;
|
|
6766
|
+
let close = end + 1;
|
|
6767
|
+
while (close < code.length && /\s/.test(code[close]))
|
|
6768
|
+
close++;
|
|
6769
|
+
if (code[close] === ")")
|
|
6770
|
+
return true;
|
|
6771
|
+
}
|
|
6772
|
+
return false;
|
|
6773
|
+
}
|
|
6545
6774
|
function isApiTest(code) {
|
|
6546
|
-
const usesRequestFixture =
|
|
6775
|
+
const usesRequestFixture = destructuresRequest(code) || /\brequest\.(get|post|put|patch|delete|head|fetch)\s*\(/.test(code);
|
|
6547
6776
|
if (!usesRequestFixture)
|
|
6548
6777
|
return false;
|
|
6549
6778
|
const drivesUi = /\bpage\.(getBy\w+|locator|click|fill|goto|waitForURL|reload)\s*\(/.test(code) || /\.(click|fill|check|uncheck|selectOption|pressSequentially)\s*\(/.test(code);
|
|
@@ -6571,7 +6800,7 @@ function roleNames(s) {
|
|
|
6571
6800
|
function interactedControlNames(lines) {
|
|
6572
6801
|
const names = /* @__PURE__ */ new Set();
|
|
6573
6802
|
for (const raw of lines) {
|
|
6574
|
-
const ln =
|
|
6803
|
+
const ln = stripLineComment2(raw);
|
|
6575
6804
|
if (!INTERACT_VERB.test(ln))
|
|
6576
6805
|
continue;
|
|
6577
6806
|
for (const n of roleNames(ln))
|
|
@@ -6767,6 +6996,14 @@ function balancedArguments(code, from, limit = code.length) {
|
|
|
6767
6996
|
}
|
|
6768
6997
|
return null;
|
|
6769
6998
|
}
|
|
6999
|
+
function expectCallTail(stmt) {
|
|
7000
|
+
const at = stmt.search(EXPECT_CALL_RE);
|
|
7001
|
+
if (at < 0)
|
|
7002
|
+
return null;
|
|
7003
|
+
const from = stmt.indexOf("(", at) + 1;
|
|
7004
|
+
const args = balancedArguments(stmt, from, MATCHER_ARGS_MAX);
|
|
7005
|
+
return args === null ? null : stmt.slice(from + args.length).trimStart();
|
|
7006
|
+
}
|
|
6770
7007
|
function matcherArgumentsAreConstant(body, from) {
|
|
6771
7008
|
MATCHER_OPEN.lastIndex = from;
|
|
6772
7009
|
const opened = MATCHER_OPEN.exec(body);
|
|
@@ -6802,34 +7039,72 @@ function hasOnlyConstantAssertions(code) {
|
|
|
6802
7039
|
const { constant, real } = constantAssertions(code);
|
|
6803
7040
|
return constant.length > 0 && real === 0;
|
|
6804
7041
|
}
|
|
7042
|
+
function envValueKey(name) {
|
|
7043
|
+
return `\0env:${name}`;
|
|
7044
|
+
}
|
|
6805
7045
|
function enteredValues(stmts) {
|
|
6806
7046
|
const values = /* @__PURE__ */ new Map();
|
|
6807
7047
|
const navLines = [];
|
|
7048
|
+
const accountConsts = /* @__PURE__ */ new Set();
|
|
6808
7049
|
for (const { stmt, startLine } of stmts) {
|
|
6809
7050
|
if (PAGE_NAV.test(stmt))
|
|
6810
7051
|
navLines.push(startLine);
|
|
7052
|
+
for (const c of stmt.matchAll(ACCOUNT_CONST))
|
|
7053
|
+
accountConsts.add(c[1].replace(/\$/g, "\\$"));
|
|
6811
7054
|
let m;
|
|
6812
7055
|
ENTERED_VALUE.lastIndex = 0;
|
|
6813
|
-
|
|
6814
|
-
const
|
|
6815
|
-
if (!
|
|
6816
|
-
values.set(value, startLine);
|
|
6817
|
-
|
|
7056
|
+
const add = (value) => {
|
|
7057
|
+
const lines = values.get(value);
|
|
7058
|
+
if (!lines)
|
|
7059
|
+
values.set(value, [startLine]);
|
|
7060
|
+
else if (lines[lines.length - 1] !== startLine)
|
|
7061
|
+
lines.push(startLine);
|
|
7062
|
+
};
|
|
7063
|
+
while (m = ENTERED_VALUE.exec(stmt))
|
|
7064
|
+
add((m[1] ?? m[2] ?? m[3]).toLowerCase());
|
|
7065
|
+
ENTERED_ENV.lastIndex = 0;
|
|
7066
|
+
while (m = ENTERED_ENV.exec(stmt))
|
|
7067
|
+
add(envValueKey(m[1] ?? m[2] ?? m[3] ?? m[4]));
|
|
6818
7068
|
}
|
|
6819
|
-
|
|
7069
|
+
const consts = accountConsts.size > 0 ? `|(?<![\\w$.])(?:${[...accountConsts].join("|")})(?![\\w$])` : "";
|
|
7070
|
+
return { values, navLines, account: new RegExp(`${ACCOUNT_ENV}${consts}`) };
|
|
6820
7071
|
}
|
|
6821
|
-
function
|
|
7072
|
+
function countBelow(lines, line) {
|
|
7073
|
+
let lo = 0;
|
|
7074
|
+
let hi = lines.length;
|
|
7075
|
+
while (lo < hi) {
|
|
7076
|
+
const mid = lo + hi >> 1;
|
|
7077
|
+
if (lines[mid] < line)
|
|
7078
|
+
lo = mid + 1;
|
|
7079
|
+
else
|
|
7080
|
+
hi = mid;
|
|
7081
|
+
}
|
|
7082
|
+
return lo;
|
|
7083
|
+
}
|
|
7084
|
+
function enteredLinesOf(stmt, entered) {
|
|
6822
7085
|
const m = stmt.match(/\.toHaveValue\s*\(\s*(['"`])((?:(?!\1).)+)\1/);
|
|
6823
|
-
if (
|
|
7086
|
+
if (m)
|
|
7087
|
+
return entered.values.get(m[2].toLowerCase());
|
|
7088
|
+
const env = stmt.match(HAVE_VALUE_ENV);
|
|
7089
|
+
return env ? entered.values.get(envValueKey(env[1] ?? env[2])) : void 0;
|
|
7090
|
+
}
|
|
7091
|
+
function typedSinceLastNav(stmt, entered, startLine, lines = enteredLinesOf(stmt, entered)) {
|
|
7092
|
+
if (!lines || startLine === void 0)
|
|
6824
7093
|
return false;
|
|
6825
|
-
const
|
|
6826
|
-
if (
|
|
7094
|
+
const before = countBelow(lines, startLine);
|
|
7095
|
+
if (before === 0)
|
|
6827
7096
|
return false;
|
|
6828
|
-
|
|
6829
|
-
return true;
|
|
6830
|
-
return !entered.navLines.some((n) => n > enteredAt && n < startLine);
|
|
7097
|
+
return countBelow(entered.navLines, startLine) === countBelow(entered.navLines, lines[before - 1] + 1);
|
|
6831
7098
|
}
|
|
6832
|
-
function
|
|
7099
|
+
function isEnteredValueEcho(stmt, entered, startLine) {
|
|
7100
|
+
const lines = enteredLinesOf(stmt, entered);
|
|
7101
|
+
if (!lines)
|
|
7102
|
+
return false;
|
|
7103
|
+
if (startLine === void 0 || lines[0] >= startLine)
|
|
7104
|
+
return true;
|
|
7105
|
+
return typedSinceLastNav(stmt, entered, startLine, lines);
|
|
7106
|
+
}
|
|
7107
|
+
function isAnyValueMatcher(stmt) {
|
|
6833
7108
|
if (/\.not\.toHaveValue\s*\(\s*(?:['"`]{2}|\/\^?\$?\/)/.test(stmt))
|
|
6834
7109
|
return true;
|
|
6835
7110
|
const m = stmt.match(/\.toHaveValue\s*\(\s*\/((?:\\.|[^/\\]){1,300})\/[a-z]*/);
|
|
@@ -6917,6 +7192,49 @@ function hasAttributeTransition(stmts) {
|
|
|
6917
7192
|
return true;
|
|
6918
7193
|
return false;
|
|
6919
7194
|
}
|
|
7195
|
+
function comparesWithItself(subject, tail) {
|
|
7196
|
+
const m = SELF_COMPARE_MATCHER.exec(tail);
|
|
7197
|
+
if (!m)
|
|
7198
|
+
return false;
|
|
7199
|
+
const args = balancedArguments(tail, m[0].length, MATCHER_ARGS_MAX);
|
|
7200
|
+
if (args === null)
|
|
7201
|
+
return false;
|
|
7202
|
+
const squash2 = (x) => x.replace(/^\s*await\s+/, "").replace(/\s+/g, "");
|
|
7203
|
+
const expected = squash2(args);
|
|
7204
|
+
return expected === squash2(m[1] === "toHaveCount" ? `${subject}.count()` : subject);
|
|
7205
|
+
}
|
|
7206
|
+
function asPlainAssertion(s) {
|
|
7207
|
+
return plainAssertion(s, false);
|
|
7208
|
+
}
|
|
7209
|
+
function plainAssertion(s, keepSelfCompare) {
|
|
7210
|
+
const { stmt } = s;
|
|
7211
|
+
if (COUNT_AT_LEAST_ZERO.test(stmt))
|
|
7212
|
+
return [];
|
|
7213
|
+
const subject = extractExpectSubject(stmt);
|
|
7214
|
+
if (subject === null)
|
|
7215
|
+
return [s];
|
|
7216
|
+
const plain = subject.replace(/^await\s+/, "").trim();
|
|
7217
|
+
const subjectAt = stmt.indexOf(subject, stmt.search(EXPECT_CALL_RE));
|
|
7218
|
+
const tail = (stmt.startsWith("expect.poll", stmt.search(EXPECT_CALL_RE)) ? expectCallTail(stmt) : null) ?? stmt.slice(subjectAt + subject.length).trimStart();
|
|
7219
|
+
const as = (rewritten) => [{ ...s, stmt: rewritten }];
|
|
7220
|
+
if (LOCATOR_EXPR.test(plain) && !LOCATOR_VALUE_READ.test(plain) && ALWAYS_TRUE_ON_OBJECT.test(tail))
|
|
7221
|
+
return [];
|
|
7222
|
+
if (DEFINED_VALUE_READ.test(plain) && ALWAYS_DEFINED.test(tail))
|
|
7223
|
+
return [];
|
|
7224
|
+
if (!keepSelfCompare && comparesWithItself(plain, tail))
|
|
7225
|
+
return [];
|
|
7226
|
+
const visible = /^(.+)\.\s*isVisible\s*\(\s*\)$/s.exec(plain);
|
|
7227
|
+
if (visible && IS_VISIBLE_TRUE.test(tail))
|
|
7228
|
+
return as(`await expect(${visible[1]}).toBeVisible();`);
|
|
7229
|
+
if (/^page\s*\.\s*(?:url|title)\s*\(\s*\)$/.test(plain) && PAGE_VALUE_MATCHER.test(tail)) {
|
|
7230
|
+
const matcher = plain.includes("url") ? "toHaveURL" : "toHaveTitle";
|
|
7231
|
+
return as(`await expect(page)${tail.replace(/^\)\s*\.\s*\w+\s*\(/, `.${matcher}(`)}`);
|
|
7232
|
+
}
|
|
7233
|
+
return [s];
|
|
7234
|
+
}
|
|
7235
|
+
function assertionStrengthHoistedReadsOn(value = typeof process !== "undefined" ? process.env?.ASSERTION_STRENGTH_HOISTED_READS : void 0) {
|
|
7236
|
+
return /^(1|true|on)$/i.test((value ?? "").trim());
|
|
7237
|
+
}
|
|
6920
7238
|
function creditsOutcomeAlways(stmt, entered, startLine) {
|
|
6921
7239
|
if (!OUTCOME_ALWAYS.some((re) => re.test(stmt)))
|
|
6922
7240
|
return false;
|
|
@@ -6949,6 +7267,22 @@ function successNoticeQuote(stmt) {
|
|
|
6949
7267
|
}
|
|
6950
7268
|
return null;
|
|
6951
7269
|
}
|
|
7270
|
+
function namedPagePath(arg) {
|
|
7271
|
+
const text = arg.trim();
|
|
7272
|
+
let path;
|
|
7273
|
+
const quoted = /^(['"`])([^'"`]*)\1$/.exec(text);
|
|
7274
|
+
const regex = quoted ? null : /^\/((?:\\.|[^/\\])+)\/[a-z]*$/.exec(text);
|
|
7275
|
+
if (quoted) {
|
|
7276
|
+
path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\*\*$/, "");
|
|
7277
|
+
} else if (regex) {
|
|
7278
|
+
path = regex[1].replace(/^\^/, "").replace(/^\.\*/, "").replace(/\$$/, "").replace(/\\([/.-])/g, "$1");
|
|
7279
|
+
} else {
|
|
7280
|
+
return null;
|
|
7281
|
+
}
|
|
7282
|
+
if (!PLAIN_PAGE_PATH.test(path))
|
|
7283
|
+
return null;
|
|
7284
|
+
return path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
7285
|
+
}
|
|
6952
7286
|
function provesStoredControlValue(stmt, startLine, persistence) {
|
|
6953
7287
|
if (!persistence || startLine === void 0)
|
|
6954
7288
|
return false;
|
|
@@ -6975,7 +7309,64 @@ function reopenedAfterSave(startLine, persistence, reopens = persistence.reloadL
|
|
|
6975
7309
|
}
|
|
6976
7310
|
return lo < reopens.length && reopens[lo] < startLine;
|
|
6977
7311
|
}
|
|
6978
|
-
function
|
|
7312
|
+
function actsBetween(withLines, from, to) {
|
|
7313
|
+
let acts = actLinesOf.get(withLines);
|
|
7314
|
+
if (acts === void 0) {
|
|
7315
|
+
acts = withLines.filter((s) => ACTS.test(s.stmt)).map((s) => s.startLine).sort((a, b) => a - b);
|
|
7316
|
+
actLinesOf.set(withLines, acts);
|
|
7317
|
+
}
|
|
7318
|
+
let lo = 0;
|
|
7319
|
+
let hi = acts.length;
|
|
7320
|
+
while (lo < hi) {
|
|
7321
|
+
const mid = lo + hi >> 1;
|
|
7322
|
+
if (acts[mid] <= from)
|
|
7323
|
+
lo = mid + 1;
|
|
7324
|
+
else
|
|
7325
|
+
hi = mid;
|
|
7326
|
+
}
|
|
7327
|
+
return lo < acts.length && acts[lo] < to;
|
|
7328
|
+
}
|
|
7329
|
+
function signInProofWindow(stmts, withLines, lines, blockIds) {
|
|
7330
|
+
if (blockIds === null)
|
|
7331
|
+
return void 0;
|
|
7332
|
+
const testBodies2 = /* @__PURE__ */ new Set();
|
|
7333
|
+
for (let i = 0; i < lines.length; i++) {
|
|
7334
|
+
const id = blockIds[i];
|
|
7335
|
+
if (id > 0 && (i === 0 || blockIds[i - 1] !== id) && TEST_BODY_OPEN.test(stripLineComment2(lines[i]).trim()))
|
|
7336
|
+
testBodies2.add(id);
|
|
7337
|
+
}
|
|
7338
|
+
const blockOf = (i) => blockIds[withLines[i].startLine - 1];
|
|
7339
|
+
const passwordAt = stmts.findIndex((stmt, i) => testBodies2.has(blockOf(i)) && ENV_PASSWORD_ENTRY.test(stmt));
|
|
7340
|
+
if (passwordAt < 0)
|
|
7341
|
+
return void 0;
|
|
7342
|
+
const body = blockOf(passwordAt);
|
|
7343
|
+
let clickAt = passwordAt + 1;
|
|
7344
|
+
while (clickAt < stmts.length && FORM_INPUT.test(stmts[clickAt]) && !ANY_CLICK.test(stmts[clickAt]))
|
|
7345
|
+
clickAt++;
|
|
7346
|
+
if (clickAt >= stmts.length || blockOf(clickAt) !== body || !ANY_CLICK.test(stmts[clickAt]) || NAV_LOCATOR.test(stmts[clickAt]))
|
|
7347
|
+
return void 0;
|
|
7348
|
+
let end = clickAt + 1;
|
|
7349
|
+
for (; end < stmts.length && blockOf(end) === body; end++) {
|
|
7350
|
+
if (CLOSERS_ONLY.test(stmts[end]))
|
|
7351
|
+
continue;
|
|
7352
|
+
if (!ASSERT_OR_WAIT.test(stmts[end]) || ACTS.test(stmts[end]))
|
|
7353
|
+
return void 0;
|
|
7354
|
+
}
|
|
7355
|
+
return { after: withLines[clickAt].startLine, until: end < stmts.length ? withLines[end].startLine : Number.POSITIVE_INFINITY };
|
|
7356
|
+
}
|
|
7357
|
+
function isAccountControlCheck(stmt) {
|
|
7358
|
+
if (!VISIBILITY_MATCHER.test(stmt) || NEGATED_ASSERT.test(stmt) || OPTION_ABSENCE.test(stmt))
|
|
7359
|
+
return false;
|
|
7360
|
+
const role = ACCOUNT_CONTROL_ROLE.exec(stmt);
|
|
7361
|
+
if (!role)
|
|
7362
|
+
return false;
|
|
7363
|
+
const rest = stmt.slice(role.index + role[0].length, role.index + role[0].length + 300);
|
|
7364
|
+
const close = rest.indexOf("}");
|
|
7365
|
+
const options = close < 0 ? rest : rest.slice(0, close);
|
|
7366
|
+
const name = options.match(/(?:^|[\s,])name\s*:\s*/);
|
|
7367
|
+
return name !== null && ACCOUNT_EMAIL_ENV.test(options.slice(name.index + name[0].length));
|
|
7368
|
+
}
|
|
7369
|
+
function classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstMutLine, persistence, signedIn) {
|
|
6979
7370
|
if (mutated && hasAttributeTransition(stmts))
|
|
6980
7371
|
return { kind: "durable" };
|
|
6981
7372
|
const notices = [];
|
|
@@ -6985,11 +7376,25 @@ function classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstM
|
|
|
6985
7376
|
};
|
|
6986
7377
|
let presenceText;
|
|
6987
7378
|
const presence = () => presenceText ??= stmts.filter((o) => !ABSENCE_MATCHER.test(o.stmt) && !NEGATED_ASSERT.test(o.stmt)).map((o) => o.stmt).join("\n");
|
|
7379
|
+
let shownAccounts;
|
|
7380
|
+
const shown = () => {
|
|
7381
|
+
if (shownAccounts)
|
|
7382
|
+
return shownAccounts;
|
|
7383
|
+
const ref = new RegExp(entered.account.source, "g");
|
|
7384
|
+
return shownAccounts = new Set(stmts.filter((o) => !claimsAbsence(o.stmt)).flatMap((o) => Array.from(o.stmt.matchAll(ref), (m) => m[0])));
|
|
7385
|
+
};
|
|
6988
7386
|
for (const { stmt, startLine } of stmts) {
|
|
6989
7387
|
if (firstMutLine !== void 0 && startLine !== void 0 && startLine < firstMutLine)
|
|
6990
7388
|
continue;
|
|
6991
7389
|
if (isVacuousErrorAbsence(stmt, presence))
|
|
6992
7390
|
continue;
|
|
7391
|
+
if (COUNT_ONE.test(stmt) && quotedAssertedStrings(stmt).some((t) => clicked.has(t.toLowerCase())))
|
|
7392
|
+
continue;
|
|
7393
|
+
if (isVacuousAccountAbsence(stmt, shown, entered.account))
|
|
7394
|
+
continue;
|
|
7395
|
+
if (signedIn && startLine !== void 0 && startLine > signedIn.after && startLine < signedIn.until && isAccountControlCheck(stmt)) {
|
|
7396
|
+
return { kind: "durable" };
|
|
7397
|
+
}
|
|
6993
7398
|
if (creditsOutcomeAlways(stmt, entered, startLine))
|
|
6994
7399
|
return { kind: "durable" };
|
|
6995
7400
|
if (mutated && !NEGATED_ASSERT.test(stmt) && MESSAGE_SURFACE.test(stmt) && OUTCOME_TEXT.test(assertedText(stmt))) {
|
|
@@ -6999,12 +7404,15 @@ function classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstM
|
|
|
6999
7404
|
else
|
|
7000
7405
|
return { kind: "durable" };
|
|
7001
7406
|
}
|
|
7002
|
-
|
|
7407
|
+
const retyped = typedSinceLastNav(stmt, entered, startLine);
|
|
7408
|
+
if (!retyped && STATE_MATCHER.test(stmt) && (persistence && startLine !== void 0 ? reopenedAfterSave(startLine, persistence, persistence.reloadCalls) : reloaded))
|
|
7003
7409
|
return { kind: "durable" };
|
|
7004
|
-
if (provesStoredControlValue(stmt, startLine, persistence))
|
|
7410
|
+
if (!retyped && provesStoredControlValue(stmt, startLine, persistence))
|
|
7005
7411
|
return { kind: "durable" };
|
|
7006
7412
|
if (countsAsContentText(stmt))
|
|
7007
7413
|
return { kind: "durable" };
|
|
7414
|
+
if (provesTextRemoved(stmt, presence))
|
|
7415
|
+
return { kind: "durable" };
|
|
7008
7416
|
if (mutated && VISIBILITY_MATCHER.test(stmt)) {
|
|
7009
7417
|
const revealsText = GET_BY_TEXT.test(stmt) || HAS_TEXT_FILTER.test(stmt) || CONTENT_ROLE.test(stmt);
|
|
7010
7418
|
if (revealsText && !isChromeTarget(stmt)) {
|
|
@@ -7068,10 +7476,57 @@ function noticeOnlyExemption(clicked, noticeText, interactionStmts) {
|
|
|
7068
7476
|
function detectOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstMutLine) {
|
|
7069
7477
|
return classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstMutLine).kind !== "none";
|
|
7070
7478
|
}
|
|
7479
|
+
function clickLabel(stmt) {
|
|
7480
|
+
let label = null;
|
|
7481
|
+
for (const m of stmt.matchAll(CONTROL_LABEL))
|
|
7482
|
+
label = m[2] ?? m[3] ?? null;
|
|
7483
|
+
return label;
|
|
7484
|
+
}
|
|
7485
|
+
function teardownLines(lines) {
|
|
7486
|
+
const out = new Array(lines.length).fill(false);
|
|
7487
|
+
let open = false;
|
|
7488
|
+
let depth = 0;
|
|
7489
|
+
for (let i = 0; i < lines.length; i++) {
|
|
7490
|
+
const ln = stripLineComment2(lines[i]).trim();
|
|
7491
|
+
let from = 0;
|
|
7492
|
+
if (!open) {
|
|
7493
|
+
const m = TEARDOWN_OPEN.exec(ln);
|
|
7494
|
+
if (!m)
|
|
7495
|
+
continue;
|
|
7496
|
+
open = true;
|
|
7497
|
+
depth = 0;
|
|
7498
|
+
from = m.index;
|
|
7499
|
+
}
|
|
7500
|
+
out[i] = true;
|
|
7501
|
+
let closed = false;
|
|
7502
|
+
let quote = null;
|
|
7503
|
+
for (let k = from; k < ln.length; k++) {
|
|
7504
|
+
const ch = ln[k];
|
|
7505
|
+
if (quote !== null) {
|
|
7506
|
+
if (ch === "\\")
|
|
7507
|
+
k++;
|
|
7508
|
+
else if (ch === quote)
|
|
7509
|
+
quote = null;
|
|
7510
|
+
continue;
|
|
7511
|
+
}
|
|
7512
|
+
if (ch === "'" || ch === '"' || ch === "`")
|
|
7513
|
+
quote = ch;
|
|
7514
|
+
else if (ch === "{")
|
|
7515
|
+
depth++;
|
|
7516
|
+
else if (ch === "}") {
|
|
7517
|
+
depth--;
|
|
7518
|
+
closed = true;
|
|
7519
|
+
}
|
|
7520
|
+
}
|
|
7521
|
+
if (depth <= 0 && closed)
|
|
7522
|
+
open = false;
|
|
7523
|
+
}
|
|
7524
|
+
return out;
|
|
7525
|
+
}
|
|
7071
7526
|
function validateAssertionStrength(code, opts = {}) {
|
|
7072
7527
|
const body = stripLoginScaffolding(code);
|
|
7073
7528
|
const lines = body.split("\n");
|
|
7074
|
-
const stmts = liveExpectStatements(stripCoverageComments(body));
|
|
7529
|
+
const stmts = liveExpectStatements(stripCoverageComments(body)).flatMap(asPlainAssertion);
|
|
7075
7530
|
const base = (extra) => ({
|
|
7076
7531
|
apiTest: false,
|
|
7077
7532
|
interacted: false,
|
|
@@ -7111,7 +7566,13 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
7111
7566
|
const interactionStmts = inlineLocatorBindings(interactionStmtsRaw);
|
|
7112
7567
|
const blockIds = testBlockIds(lines);
|
|
7113
7568
|
const blockOfLine = (line) => blockIds?.[line - 1] ?? 0;
|
|
7114
|
-
const
|
|
7569
|
+
const hoistedReads = assertionStrengthHoistedReadsOn();
|
|
7570
|
+
const boundAt = hoistedReads ? /* @__PURE__ */ new Map() : void 0;
|
|
7571
|
+
const resolved = resolveSubjectBindings(stmts, scopedLocatorBindings(interactionStmtsWithLines, blockOfLine), blockOfLine, boundAt);
|
|
7572
|
+
const checks = boundAt === void 0 ? resolved : resolved.flatMap((s) => {
|
|
7573
|
+
const from = boundAt.get(s);
|
|
7574
|
+
return plainAssertion(s, from !== void 0 && actsBetween(interactionStmtsWithLines, from, s.startLine));
|
|
7575
|
+
});
|
|
7115
7576
|
const insideTest = linesInsideTestBlocks(lines);
|
|
7116
7577
|
const inTestOrder = (i) => insideTest === null || insideTest[interactionStmtsWithLines[i].startLine - 1] === true;
|
|
7117
7578
|
let mutated = opts.interactionPerformed === true;
|
|
@@ -7121,9 +7582,34 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
7121
7582
|
let navClicks = 0;
|
|
7122
7583
|
const persistence = { mutLines: [], reloadLines: [], reloadCalls: [] };
|
|
7123
7584
|
const openedPages = /* @__PURE__ */ new Set();
|
|
7585
|
+
let checkedPage = null;
|
|
7586
|
+
const savedOnCheckedPage = /* @__PURE__ */ new Set();
|
|
7587
|
+
const saveAt = (i, at) => {
|
|
7588
|
+
lastMutIdx = i;
|
|
7589
|
+
if (!inTestOrder(i))
|
|
7590
|
+
return;
|
|
7591
|
+
persistence.mutLines.push(at);
|
|
7592
|
+
if (firstMutIdx < 0)
|
|
7593
|
+
firstMutIdx = i;
|
|
7594
|
+
if (checkedPage !== null)
|
|
7595
|
+
savedOnCheckedPage.add(checkedPage);
|
|
7596
|
+
};
|
|
7597
|
+
const lastActionProof = opts.lastActionProof ?? "off";
|
|
7598
|
+
const teardown = lastActionProof === "off" ? null : teardownLines(lines);
|
|
7599
|
+
const acts = { last: null, prev: null, count: 0 };
|
|
7600
|
+
const noteAct = (i, at, kind) => {
|
|
7601
|
+
if (teardown === null || teardown[at - 1] || !inTestOrder(i))
|
|
7602
|
+
return;
|
|
7603
|
+
if (kind !== "enter")
|
|
7604
|
+
acts.count++;
|
|
7605
|
+
acts.prev = acts.last;
|
|
7606
|
+
acts.last = { i, kind };
|
|
7607
|
+
};
|
|
7124
7608
|
for (let i = 0; i < interactionStmts.length; i++) {
|
|
7125
7609
|
const ln = interactionStmts[i];
|
|
7126
7610
|
const at = interactionStmtsWithLines[i].startLine;
|
|
7611
|
+
if (teardown !== null && acts.last?.kind === "fill" && ENTER_PRESS.test(ln))
|
|
7612
|
+
noteAct(i, at, "enter");
|
|
7127
7613
|
if (RELOAD.test(ln)) {
|
|
7128
7614
|
persistence.reloadLines.push(at);
|
|
7129
7615
|
persistence.reloadCalls.push(at);
|
|
@@ -7131,32 +7617,31 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
7131
7617
|
const opened = ln.match(/\.goto\s*\(\s*([^,)]+)/);
|
|
7132
7618
|
if (opened) {
|
|
7133
7619
|
const page = opened[1].trim();
|
|
7134
|
-
|
|
7620
|
+
const path = namedPagePath(page);
|
|
7621
|
+
if (openedPages.has(page) || path !== null && savedOnCheckedPage.has(path))
|
|
7135
7622
|
persistence.reloadLines.push(at);
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
}
|
|
7623
|
+
openedPages.add(page);
|
|
7624
|
+
checkedPage = null;
|
|
7625
|
+
}
|
|
7626
|
+
if (/\bpage\s*\.\s*go(?:Back|Forward)\s*\(/.test(ln))
|
|
7627
|
+
checkedPage = null;
|
|
7628
|
+
const checked = URL_CHECK_ARG.exec(ln);
|
|
7629
|
+
if (checked)
|
|
7630
|
+
checkedPage = namedPagePath(checked[1] ?? checked[2]);
|
|
7139
7631
|
if (FORM_INPUT.test(ln)) {
|
|
7140
7632
|
mutated = true;
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
persistence.mutLines.push(at);
|
|
7144
|
-
if (firstMutIdx < 0)
|
|
7145
|
-
firstMutIdx = i;
|
|
7146
|
-
}
|
|
7633
|
+
saveAt(i, at);
|
|
7634
|
+
noteAct(i, at, "fill");
|
|
7147
7635
|
continue;
|
|
7148
7636
|
}
|
|
7149
7637
|
if (ANY_CLICK.test(ln)) {
|
|
7150
7638
|
if (NAV_LOCATOR.test(ln)) {
|
|
7151
7639
|
navClicks++;
|
|
7640
|
+
checkedPage = null;
|
|
7152
7641
|
} else {
|
|
7153
7642
|
mutated = true;
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
persistence.mutLines.push(at);
|
|
7157
|
-
if (firstMutIdx < 0)
|
|
7158
|
-
firstMutIdx = i;
|
|
7159
|
-
}
|
|
7643
|
+
saveAt(i, at);
|
|
7644
|
+
noteAct(i, at, "click");
|
|
7160
7645
|
}
|
|
7161
7646
|
}
|
|
7162
7647
|
}
|
|
@@ -7189,9 +7674,28 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
7189
7674
|
};
|
|
7190
7675
|
}
|
|
7191
7676
|
const orderedPersistence = firstMutLine !== void 0 ? persistence : void 0;
|
|
7192
|
-
const
|
|
7677
|
+
const signedIn = signInProofWindow(interactionStmts, interactionStmtsWithLines, lines, blockIds);
|
|
7678
|
+
const proof = classifyOutcomeProof(checks, mutated, reloaded, clickedControlNames(interactionStmts), entered, firstMutLine, orderedPersistence, signedIn);
|
|
7193
7679
|
if (proof.kind === "durable") {
|
|
7194
7680
|
const signals2 = base({ interacted, mutated, navOnly, hasOutcomeProof: true, terminal });
|
|
7681
|
+
const { last, prev } = acts;
|
|
7682
|
+
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])))) {
|
|
7683
|
+
const lastLine = interactionStmtsWithLines[last.i].startLine;
|
|
7684
|
+
const clicked = clickedControlNames(interactionStmts);
|
|
7685
|
+
const after = classifyOutcomeProof(checks, mutated, reloaded, clicked, entered, lastLine, orderedPersistence, signedIn);
|
|
7686
|
+
const unproven = after.kind === "none" || after.kind === "notice" && noticeOnlyExemption(clicked, after.noticeText, interactionStmts) === null;
|
|
7687
|
+
if (unproven && lastActionProof === "on") {
|
|
7688
|
+
const line = (keptScaffoldingLines(code.split("\n"))[lastLine - 1] ?? lastLine - 1) + 1;
|
|
7689
|
+
return {
|
|
7690
|
+
ok: false,
|
|
7691
|
+
severity: "block",
|
|
7692
|
+
reason: `the last save (line ${line}) has no outcome check after it (the earlier check proves only the earlier action)`,
|
|
7693
|
+
signals: { ...signals2, lastActionUnproven: true }
|
|
7694
|
+
};
|
|
7695
|
+
}
|
|
7696
|
+
if (unproven)
|
|
7697
|
+
signals2.lastActionUnproven = true;
|
|
7698
|
+
}
|
|
7195
7699
|
return { ok: true, severity: "none", reason: "proves an outcome (count / diff / value / revealed content)", signals: signals2 };
|
|
7196
7700
|
}
|
|
7197
7701
|
if (mutated && proof.kind === "notice") {
|
|
@@ -7240,7 +7744,7 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
7240
7744
|
signals
|
|
7241
7745
|
};
|
|
7242
7746
|
}
|
|
7243
|
-
const urlProofAfter = interactionStmts.some((s, i) => i > lastMutIdx &&
|
|
7747
|
+
const urlProofAfter = interactionStmts.some((s, i) => i > lastMutIdx && isUrlProof(s));
|
|
7244
7748
|
if (urlProofAfter) {
|
|
7245
7749
|
return {
|
|
7246
7750
|
ok: false,
|
|
@@ -7277,7 +7781,7 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
7277
7781
|
signals
|
|
7278
7782
|
};
|
|
7279
7783
|
}
|
|
7280
|
-
var ERROR_BANNER, OUTCOME_ALWAYS, FORM_INPUT, ANY_CLICK, NAV_LOCATOR, URL_PROOF, RELOAD, EXPECT_STMT, BLOCK_SCAFFOLD2, TEST_BLOCK_OPEN, LOCATOR_BINDING, EXPECT_OPEN, STATE_MATCHER, CHROME_LOCATOR, CONTENT_ROLE, GET_BY_TEXT, BARE_CONTAINER_ROLE, HAS_TEXT_FILTER, OUTCOME_TEXT, SUCCESS_NOTICE_TEXT, NOT_A_SUCCESS_NOTICE, ANNOUNCE_ROLE, MESSAGE_SURFACE, TEXT_CONTENT_MATCHER, VISIBILITY_MATCHER, ADDITIVE_AFFORDANCE, EXCLUDE_AFFORDANCE, EMPTY_STATE_TEXT, AUTH_START, AUTH_END, LOGIN_CALL_LINE, INBOX_START, INBOX_END, NEGATED_ASSERT, CONST_CHAIN_MAX_DEPTH, TYPED_INPUT, TITLE_LINE, FAILURE_STATUS_ASSERT, FAILED_LIST_ASSERT, ENDPOINT_DOWN, NAVIGATION_RESPONSE_VAR, DIRECT_REQUEST_VAR, CATCH_ALL_PATTERN, GUARD_HANDLER, ABSENCE_MATCHER, ERROR_TEXT_WORDS, TEXT_REGEX_ARG, ROLE_NAME_RE, INTERACT_VERB, ROLE_SIG_RE, TRANSIENT_CONTROL_ROLES, POLL_CALLBACK, CONSTANT_WORD, CONSTANT_GLUE_CHARS, WORD_CHAR, MATCHER_ARGS_MAX, MATCHER_OPEN, ENTERED_VALUE, PAGE_NAV, IN_FLIGHT_TEXT, FORM_CONTROL_LOCATOR, CONTROL_VALUE_MATCHER, COPY_CONTROL, COPIED_NOTICE, SEND_CONTROL, SEND_NOTICE, MARK_READ_CONTROL, APP_DATA_FIELD;
|
|
7784
|
+
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;
|
|
7281
7785
|
var init_assertion_strength = __esm({
|
|
7282
7786
|
"../shared/dist/assertion-strength.js"() {
|
|
7283
7787
|
"use strict";
|
|
@@ -7286,6 +7790,7 @@ var init_assertion_strength = __esm({
|
|
|
7286
7790
|
init_assertion_proof();
|
|
7287
7791
|
init_auth_handoff_coverage();
|
|
7288
7792
|
init_expect_call();
|
|
7793
|
+
init_auth_plan();
|
|
7289
7794
|
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;
|
|
7290
7795
|
OUTCOME_ALWAYS = [
|
|
7291
7796
|
/\.toHaveCount\s*\(/,
|
|
@@ -7302,8 +7807,8 @@ var init_assertion_strength = __esm({
|
|
|
7302
7807
|
// form field value
|
|
7303
7808
|
/\.toMatch\s*\(/,
|
|
7304
7809
|
// string/regex content match (e.g. textContent() vs /[2-9] selected/)
|
|
7305
|
-
/\.toBeDisabled\s*\(/,
|
|
7306
|
-
// a control became DISABLED — a gating/validation outcome
|
|
7810
|
+
/\.(?<!\.not\s*\.)\s*toBeDisabled\s*\(/,
|
|
7811
|
+
// a control became DISABLED — a gating/validation outcome (not.toBeDisabled is the toBeEnabled twin)
|
|
7307
7812
|
/\.not\.toBeVisible\s*\(/,
|
|
7308
7813
|
/\.toBeHidden\s*\(/
|
|
7309
7814
|
// something the action removed/closed (disappearance proof)
|
|
@@ -7311,12 +7816,16 @@ var init_assertion_strength = __esm({
|
|
|
7311
7816
|
FORM_INPUT = /\.(fill|selectOption|setInputFiles|check|uncheck|setChecked|pressSequentially)\s*\(|keyboard\.type\s*\(/;
|
|
7312
7817
|
ANY_CLICK = /\.click\s*\(/;
|
|
7313
7818
|
NAV_LOCATOR = /getByRole\s*\(\s*['"`](?:link|menuitem)['"`]/i;
|
|
7314
|
-
URL_PROOF = /\.toHaveURL\s*\(|\.waitForURL\s*\(/;
|
|
7819
|
+
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*\(/;
|
|
7820
|
+
POLL_OPEN = /\bexpect\s*\.\s*poll\s*\(/g;
|
|
7821
|
+
POLL_PAGE_URL = /^page\s*\.\s*url\s*\(\s*\)/;
|
|
7822
|
+
POLL_PAGE_URL_ONLY = /^page\s*\.\s*url\s*\(\s*\)$/;
|
|
7823
|
+
POLL_CALLS_MAX = 8;
|
|
7315
7824
|
RELOAD = /\.reload\s*\(/;
|
|
7316
7825
|
EXPECT_STMT = /\bexpect\s*[.(]/;
|
|
7317
7826
|
BLOCK_SCAFFOLD2 = /=>|\b(?:test|describe|it|beforeEach|afterEach|beforeAll|afterAll|function)\b/;
|
|
7318
7827
|
TEST_BLOCK_OPEN = /^(?:test|it)(?:\.(?:only|skip|fixme|slow|serial|describe\.serial))?\s*\(|^(?:test\.)?(?:beforeEach|beforeAll)\s*\(/;
|
|
7319
|
-
LOCATOR_BINDING = /^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s
|
|
7828
|
+
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*\(.+)$/;
|
|
7320
7829
|
EXPECT_OPEN = /\bexpect(?:\.soft)?\s*\(/;
|
|
7321
7830
|
STATE_MATCHER = /\.(toBeChecked|toBeVisible|toHaveValue|toBeDisabled|toBeEnabled)\s*\(/;
|
|
7322
7831
|
CHROME_LOCATOR = /getByRole\s*\(\s*['"`](?:heading|navigation|banner|main|contentinfo|complementary|region|search|tablist|tab|button|link|menuitem|textbox|searchbox|combobox|spinbutton)['"`]/i;
|
|
@@ -7336,16 +7845,17 @@ var init_assertion_strength = __esm({
|
|
|
7336
7845
|
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;
|
|
7337
7846
|
AUTH_START = /^\s*\/\/\s*>>>\s*VALIDATE_QA_AUTH/;
|
|
7338
7847
|
AUTH_END = /^\s*\/\/\s*<<<\s*VALIDATE_QA_AUTH/;
|
|
7339
|
-
LOGIN_CALL_LINE = /^\s*await\s+__vqaLogin\s*\(\s*page\s*\)\s
|
|
7848
|
+
LOGIN_CALL_LINE = /^\s*await\s+__vqaLogin\s*\(\s*page\s*\)\s*(?:;\s*)?$/;
|
|
7340
7849
|
INBOX_START = /^\s*\/\/\s*>>>\s*VALIDATE_QA_INBOX/;
|
|
7341
7850
|
INBOX_END = /^\s*\/\/\s*<<<\s*VALIDATE_QA_INBOX/;
|
|
7342
7851
|
NEGATED_ASSERT = /\.not\s*\.\s*to[A-Z]\w*|\.toBeHidden\s*\(/;
|
|
7852
|
+
NEGATED_ASSERT_NOT = /\.not\s*\.\s*to[A-Z]/;
|
|
7343
7853
|
CONST_CHAIN_MAX_DEPTH = 5;
|
|
7344
7854
|
TYPED_INPUT = /\.(fill|type|pressSequentially|setInputFiles)\s*\(/;
|
|
7345
7855
|
TITLE_LINE = /^\s*(?:test|describe)(?:\.\w+)?\s*\(/;
|
|
7346
7856
|
FAILURE_STATUS_ASSERT = [
|
|
7347
7857
|
/\.status\s*\(\s*\)[^;]{0,200}\.(?:toBeGreaterThanOrEqual|toBeGreaterThan)\s*\(\s*(?:399|[45]\d\d)\b/,
|
|
7348
|
-
/\bexpect\s*\(\s*[^;]{0,200}\bstatus\s*(?:\(\s*\))
|
|
7858
|
+
/\bexpect\s*\(\s*[^;]{0,200}\bstatus\s*(?:\(\s*\)\s*)?>=?\s*(?:399|[45]\d\d)\b[^;]{0,200}\)\s*\.\s*(?:toBe\s*\(\s*true|toBeTruthy)/,
|
|
7349
7859
|
/\bexpect\s*\([^;]{0,200}\.ok\s*\(\s*\)\s*\)\s*\.\s*(?:toBe\s*\(\s*false\s*\)|toBeFalsy\s*\()/
|
|
7350
7860
|
];
|
|
7351
7861
|
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;
|
|
@@ -7355,8 +7865,15 @@ var init_assertion_strength = __esm({
|
|
|
7355
7865
|
CATCH_ALL_PATTERN = /^(['"`])\*\*(?:\/\*)?\1$|^\/\.\*\/[a-z]*$/;
|
|
7356
7866
|
GUARD_HANDLER = /\bMUTATING\.has\s*\(|blockedbyclient|Read-only guard/;
|
|
7357
7867
|
ABSENCE_MATCHER = /\.not\.toBeVisible\s*\(|\.toBeHidden\s*\(|\.toHaveCount\s*\(\s*0\s*\)/;
|
|
7868
|
+
VALUE_ABSENCE = /\.(?:count\s*\(\s*\)[\s)]{0,10}\.(?:(?:toBe|toEqual|toStrictEqual)\s*\(\s*0\s*\)|toBeLessThan\s*\(\s*1\s*\))|isVisible\s*\(\s*\)[\s)]{0,10}\.(?:(?:toBe|toEqual|toStrictEqual)\s*\(\s*false\s*\)|toBeFalsy\s*\()|isHidden\s*\(\s*\)[\s)]{0,10}\.(?:(?:toBe|toEqual|toStrictEqual)\s*\(\s*true\s*\)|toBeTruthy\s*\())/;
|
|
7869
|
+
OPTION_ABSENCE = /\.(?:toBeVisible\s*\(\s*\{[^{}()]{0,200}\bvisible|toBeAttached\s*\(\s*\{[^{}()]{0,200}\battached)\s*:\s*false\b/;
|
|
7870
|
+
NOT_BEFORE_MATCHER = /\.not\s*\.\s*(?=to[A-Z])/g;
|
|
7358
7871
|
ERROR_TEXT_WORDS = /error|fail|wrong|try again|unable|could ?n.?t|can.?t|problem|invalid|unavailable/i;
|
|
7359
7872
|
TEXT_REGEX_ARG = /(?:getByText|hasText|getByRole\([^)]{0,200}name)\s*[:(]\s*(\/(?:\\.|[^/\\\n])+\/[a-z]*)/;
|
|
7873
|
+
SCOPING_STEP = /(?:\s*\.\s*(?:first|last|nth)\s*\([^()]*\))*\s*\.\s*(?:getBy[A-Z]\w*|locator|filter)\s*\(/y;
|
|
7874
|
+
NEGATED_TEXT = /\.not\s*\.\s*(?:toHaveText|toContainText)\s*\(\s*(?:['"`]([^'"`]*)['"`]|\/((?:\\.|[^/\\\n])+)\/)?/;
|
|
7875
|
+
ASYNC_DESTRUCTURE_OPEN = /async\s*\(\s*\{/g;
|
|
7876
|
+
REQUEST_WORD = /\brequest\b/;
|
|
7360
7877
|
ROLE_NAME_RE = /getByRole\s*\(\s*['"`][a-z]+['"`]\s*,\s*\{[^}]*?name\s*:\s*(?:['"`]([^'"`]+)['"`]|\/([^/]+)\/)/gi;
|
|
7361
7878
|
INTERACT_VERB = /\.(click|fill|check|uncheck|setChecked|selectOption|pressSequentially)\s*\(/;
|
|
7362
7879
|
ROLE_SIG_RE = /getByRole\s*\(\s*['"`]([a-z]+)['"`]\s*,\s*\{[^}]*?name\s*:\s*(?:['"`]([^'"`]+)['"`]|\/([^/]+)\/)/gi;
|
|
@@ -7377,15 +7894,55 @@ var init_assertion_strength = __esm({
|
|
|
7377
7894
|
MATCHER_OPEN = /\s*(?:\.\s*(?:not|resolves|rejects)\s*)*\.\s*[A-Za-z_$][\w$]*\s*\(/y;
|
|
7378
7895
|
ENTERED_VALUE = /\.(?:fill|pressSequentially)\s*\(\s*['"`]([^'"`]+)['"`]|keyboard\.type\s*\(\s*['"`]([^'"`]+)['"`]|\.selectOption\s*\(\s*(?:\[\s*)?(?:\{\s*(?:label|value)\s*:\s*)?['"`]([^'"`]+)['"`]/g;
|
|
7379
7896
|
PAGE_NAV = /\.(?:goto|reload)\s*\(/;
|
|
7897
|
+
ENV_READ = String.raw`process\.env(?:(?:\?\.|\.)([A-Za-z_]\w*)|(?:\?\.)?\[\s*['"]([A-Za-z_]\w*)['"]\s*\])`;
|
|
7898
|
+
ENTERED_ENV = new RegExp(String.raw`\.(?:fill|pressSequentially)\s*\(\s*${ENV_READ}|keyboard\.type\s*\(\s*${ENV_READ}`, "g");
|
|
7899
|
+
HAVE_VALUE_ENV = new RegExp(String.raw`\.toHaveValue\s*\(\s*${ENV_READ}`);
|
|
7900
|
+
ACCOUNT_KEYS = TEST_USER_ENV_KEYS.join("|");
|
|
7901
|
+
ACCOUNT_ENV = `process\\.env(?:\\??\\.(?:${ACCOUNT_KEYS})\\b|(?:\\?\\.)?\\[\\s*['"\`](?:${ACCOUNT_KEYS})['"\`]\\s*\\])`;
|
|
7902
|
+
ACCOUNT_CONST = new RegExp(`\\b(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)\\s*(?::\\s*string\\s*)?=\\s*${ACCOUNT_ENV}`, "g");
|
|
7380
7903
|
IN_FLIGHT_TEXT = /^(?:(?:sending|saving|loading|searching|checking|processing|submitting|preparing|updating|please wait|working|one moment)\b[^.]{0,40}\.{2,3}|\.{3}|…)$/i;
|
|
7904
|
+
LOCATOR_VALUE_READ = /\.(?:count|textContent|innerText|innerHTML|inputValue|getAttribute|isVisible|isHidden|isChecked|isEnabled|isDisabled|isEditable|allTextContents|allInnerTexts|evaluate|evaluateAll|boundingBox|all)\s*\(/;
|
|
7905
|
+
LOCATOR_EXPR = /^(?:this\.)?page\s*\.\s*(?:getBy\w+|locator)\s*\(/;
|
|
7906
|
+
ALWAYS_TRUE_ON_OBJECT = /^\)\s*\.\s*(?:toBeTruthy|toBeDefined|not\s*\.\s*(?:toBeNull|toBeUndefined|toBeFalsy))\s*\(/;
|
|
7907
|
+
COUNT_AT_LEAST_ZERO = /\.(?:count\s*\(\s*\)|length)[\s)]{0,10}\.(?:toBeGreaterThanOrEqual\s*\(\s*0\s*\)|toBeGreaterThan\s*\(\s*-\s*1\s*\))/;
|
|
7908
|
+
DEFINED_VALUE_READ = /\.\s*(?:count|textContent|innerText|innerHTML|inputValue|getAttribute|title|url|isVisible|isHidden|isChecked|isEnabled|isDisabled|isEditable|allTextContents|allInnerTexts)\s*\([^()]*\)$/;
|
|
7909
|
+
ALWAYS_DEFINED = /^\)\s*\.\s*(?:toBeDefined|not\s*\.\s*toBeUndefined)\s*\(/;
|
|
7910
|
+
SELF_COMPARE_MATCHER = /^\)\s*\.\s*(toBe|toEqual|toStrictEqual|toHaveCount)\s*\(/;
|
|
7911
|
+
IS_VISIBLE_TRUE = /^\)*\s*\.\s*(?:toBe\s*\(\s*true\s*\)|toBeTruthy\s*\(\s*\))/;
|
|
7912
|
+
PAGE_VALUE_MATCHER = /^\)\s*\.\s*(?:toBe|toEqual|toStrictEqual|toMatch|toContain)\s*\(/;
|
|
7913
|
+
COUNT_ONE = /\.\s*toHaveCount\s*\(\s*1\s*\)/;
|
|
7914
|
+
URL_CHECK_ARG = /\bpage\s*\.\s*waitForURL\s*\(\s*([^,)]+)|\bexpect(?:\s*\.\s*soft)?\s*\(\s*page\s*\)\s*\.\s*toHaveURL\s*\(\s*([^,)]+)/;
|
|
7915
|
+
PLAIN_PAGE_PATH = /^\/[\w\-./~%]*(?:[?#][^\s]*)?$/;
|
|
7381
7916
|
FORM_CONTROL_LOCATOR = /getByRole\s*\(\s*['"`](?:combobox|switch|checkbox|radio|textbox|searchbox|spinbutton|slider)['"`]|getByLabel\s*\(|getByPlaceholder\s*\(/;
|
|
7382
7917
|
CONTROL_VALUE_MATCHER = /\.(?:toHaveValue|toHaveText|toContainText)\s*\(|\.toHaveAttribute\s*\(\s*['"`]aria-checked['"`]/;
|
|
7918
|
+
ACCOUNT_EMAIL_ENV = /^process\.env(?:(?:\?\.|\.)TEST_USER_EMAIL\b|(?:\?\.)?\[\s*['"]TEST_USER_EMAIL['"]\s*\])/;
|
|
7919
|
+
ENV_PASSWORD_ENTRY = /\.(?:fill|pressSequentially)\s*\(\s*process\.env(?:(?:\?\.|\.)TEST_USER_PASSWORD\b|(?:\?\.)?\[\s*['"]TEST_USER_PASSWORD['"]\s*\])/;
|
|
7920
|
+
ACCOUNT_CONTROL_ROLE = /getByRole\s*\(\s*['"`](?:button|link|menuitem)['"`]\s*,\s*\{/;
|
|
7921
|
+
ASSERT_OR_WAIT = /^(?:await\s+)?(?:expect(?:\.soft|\.poll)?\s*\(|[\w$]+(?:\.[\w$]+)*\.waitFor[A-Za-z]*\s*\()/;
|
|
7922
|
+
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*\(/;
|
|
7923
|
+
actLinesOf = /* @__PURE__ */ new WeakMap();
|
|
7924
|
+
CLOSERS_ONLY = /^[})\];,\s]+$/;
|
|
7925
|
+
TEST_BODY_OPEN = /^(?:test|it)(?:\.(?:only|skip|fixme|slow))?\s*\(/;
|
|
7383
7926
|
COPY_CONTROL = /\bcopy\b/i;
|
|
7384
7927
|
COPIED_NOTICE = /\bcopied\b/i;
|
|
7385
7928
|
SEND_CONTROL = /\b(send|submit|subscribe|sign up for|contact|feedback|join (the )?waitlist)\b/i;
|
|
7386
7929
|
SEND_NOTICE = /\b(thank|thanks|sent|subscribed|received|in touch)\b/i;
|
|
7387
7930
|
MARK_READ_CONTROL = /\bmark(?:ed)? as read\b|\bmark all(?: as)? read\b/i;
|
|
7388
7931
|
APP_DATA_FIELD = /\b(title|name of|amount|price|quantity|sku|status|due|assignee|project|ticket|invoice|order)\b/i;
|
|
7932
|
+
COMMIT_CONTROL = /\b(?:save|submit|update|apply|add|create|post|send|publish)\b|\bconfirm\s+changes\b/i;
|
|
7933
|
+
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;
|
|
7934
|
+
DESTRUCTIVE_CONTROL = /\b(?:delete|remove|discard)\b/i;
|
|
7935
|
+
ENTER_PRESS = /\.press\s*\(\s*['"`]Enter['"`]/;
|
|
7936
|
+
CONTROL_LABEL = /(?:name\s*:\s*|getBy(?:Text|Label|Title|TestId)\s*\(\s*|has-text\(\s*)(?:(['"`])((?:(?!\1).)*)\1|\/((?:\\.|[^/\\])+)\/)/g;
|
|
7937
|
+
isCommitClick = (stmt) => {
|
|
7938
|
+
const label = clickLabel(stmt);
|
|
7939
|
+
return label !== null && COMMIT_CONTROL.test(label) && !NOT_COMMIT_CONTROL.test(label) && !EXCLUDE_AFFORDANCE.test(label);
|
|
7940
|
+
};
|
|
7941
|
+
isDestructiveClick = (stmt) => {
|
|
7942
|
+
const label = clickLabel(stmt);
|
|
7943
|
+
return label !== null && DESTRUCTIVE_CONTROL.test(label);
|
|
7944
|
+
};
|
|
7945
|
+
TEARDOWN_OPEN = /\bfinally\s*\{|^(?:test\.)?(?:after|before)(?:Each|All)\s*\(/;
|
|
7389
7946
|
}
|
|
7390
7947
|
});
|
|
7391
7948
|
|
|
@@ -8733,177 +9290,143 @@ var init_admin_view_return_to = __esm({
|
|
|
8733
9290
|
}
|
|
8734
9291
|
});
|
|
8735
9292
|
|
|
8736
|
-
// ../shared/dist/auth-
|
|
8737
|
-
function
|
|
8738
|
-
|
|
8739
|
-
return false;
|
|
8740
|
-
return /process\.env\.TEST_USER_EMAIL!?/.test(playwrightCode) && /process\.env\.TEST_USER_PASSWORD!?/.test(playwrightCode);
|
|
8741
|
-
}
|
|
8742
|
-
function hasGeneratedAuthGuard(playwrightCode) {
|
|
8743
|
-
if (!playwrightCode)
|
|
8744
|
-
return false;
|
|
8745
|
-
return /\/\/ >>> VALIDATE_QA_AUTH v(\d+)\b[\s\S]*?process\.env\.AUTH_FILE[\s\S]*?\/\/ <<< VALIDATE_QA_AUTH v\1\b/.test(playwrightCode);
|
|
9293
|
+
// ../shared/dist/auth-block-detector.js
|
|
9294
|
+
function stripEchoLines(text) {
|
|
9295
|
+
return text.split("\n").filter((line) => !ECHO_LINE_RE.test(line)).join("\n");
|
|
8746
9296
|
}
|
|
8747
|
-
function
|
|
8748
|
-
|
|
8749
|
-
return false;
|
|
8750
|
-
if (tags.includes("@auth"))
|
|
8751
|
-
return false;
|
|
8752
|
-
if (tags.includes("@api"))
|
|
8753
|
-
return true;
|
|
8754
|
-
if (!tags.includes("@needs-auth"))
|
|
8755
|
-
return false;
|
|
8756
|
-
if (sharedAuthInjectEnabled && tags.includes("@auth-guarded") && hasGeneratedAuthGuard(code))
|
|
8757
|
-
return true;
|
|
8758
|
-
const isGeneratedVariant = tags.includes("@edge") || tags.includes("@bug");
|
|
8759
|
-
const hasLegacyInlineLogin = tags.includes("@discovered") && !isGeneratedVariant && hasInlineLoginSteps(code);
|
|
8760
|
-
return !tags.includes("@inline-login") && !hasLegacyInlineLogin;
|
|
9297
|
+
function matchesAny(text, patterns) {
|
|
9298
|
+
return patterns.some((p) => p.test(text));
|
|
8761
9299
|
}
|
|
8762
|
-
function
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
if (/__vqaLogin\b/.test(code))
|
|
8766
|
-
return true;
|
|
8767
|
-
for (const m of code.matchAll(/TEST_[A-Z0-9_]+/g)) {
|
|
8768
|
-
if (/_(?:EMAIL|PASSWORD)/.test(m[0]))
|
|
8769
|
-
return true;
|
|
8770
|
-
}
|
|
8771
|
-
for (const m of code.matchAll(/process\.env\s*\[([^\]]*)\]?/g)) {
|
|
8772
|
-
if (!/^\s*(['"])[^'"`]*\1\s*$/.test(m[1]))
|
|
8773
|
-
return true;
|
|
8774
|
-
}
|
|
8775
|
-
return false;
|
|
9300
|
+
function isIntentionalNegativeAuthTest(input) {
|
|
9301
|
+
const meta = [input.testName, input.description].filter(Boolean).join(" ");
|
|
9302
|
+
return matchesAny(meta, INTENTIONAL_NEGATIVE_AUTH);
|
|
8776
9303
|
}
|
|
8777
|
-
function
|
|
8778
|
-
const
|
|
8779
|
-
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
9304
|
+
function detectAuthBlock(input) {
|
|
9305
|
+
const tags = input.tags ?? [];
|
|
9306
|
+
const isAuthed = tags.some((t) => AUTHED_TAGS.includes(t));
|
|
9307
|
+
if (!isAuthed)
|
|
9308
|
+
return NO_DETECTION;
|
|
9309
|
+
if (isIntentionalNegativeAuthTest(input))
|
|
9310
|
+
return NO_DETECTION;
|
|
9311
|
+
const sources = [];
|
|
9312
|
+
if (input.error)
|
|
9313
|
+
sources.push(stripEchoLines(input.error));
|
|
9314
|
+
if (input.logs)
|
|
9315
|
+
sources.push(stripEchoLines(input.logs));
|
|
9316
|
+
for (const sr of input.stepResults ?? []) {
|
|
9317
|
+
if (sr.error)
|
|
9318
|
+
sources.push(stripEchoLines(sr.error));
|
|
8784
9319
|
}
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
8788
|
-
|
|
8789
|
-
|
|
8790
|
-
source: "invalid",
|
|
8791
|
-
acquisition: "unsupported",
|
|
8792
|
-
presentation: "none",
|
|
8793
|
-
needsCookieState: false,
|
|
8794
|
-
needsStandingHeaders: false,
|
|
8795
|
-
standingHeadersMode: "none",
|
|
8796
|
-
needsLoginCreds: false,
|
|
8797
|
-
requiredEnvKeys: [],
|
|
8798
|
-
configurationError
|
|
8799
|
-
};
|
|
8800
|
-
}
|
|
8801
|
-
function explicitPlan(authTypeTag2, acquisition, presentation, opts) {
|
|
8802
|
-
return {
|
|
8803
|
-
version: 1,
|
|
8804
|
-
source: "explicit",
|
|
8805
|
-
authTypeTag: authTypeTag2,
|
|
8806
|
-
acquisition,
|
|
8807
|
-
presentation,
|
|
8808
|
-
needsCookieState: opts.cookie,
|
|
8809
|
-
needsStandingHeaders: opts.standingMode === "required",
|
|
8810
|
-
standingHeadersMode: opts.standingMode,
|
|
8811
|
-
needsLoginCreds: TEST_USER_ENV_KEYS.every((k) => opts.requiredEnvKeys.includes(k)),
|
|
8812
|
-
requiredEnvKeys: opts.requiredEnvKeys
|
|
8813
|
-
};
|
|
8814
|
-
}
|
|
8815
|
-
function deriveAuthPlan(tags, code, opts) {
|
|
8816
|
-
const t = Array.isArray(tags) ? tags : [];
|
|
8817
|
-
const sharedFlag = opts?.sharedAuthInjectEnabled ?? false;
|
|
8818
|
-
const isApi = t.includes("@api");
|
|
8819
|
-
const explicit = isApi ? explicitAuthTypeTags(t) : [];
|
|
8820
|
-
if (explicit.length > 0) {
|
|
8821
|
-
if (t.includes("@auth")) {
|
|
8822
|
-
return invalidPlan("@auth (self-login UI) cannot be combined with an explicit @api auth-type tag");
|
|
8823
|
-
}
|
|
8824
|
-
const loginCompose = explicit.filter((e) => e === AUTH_TAG_COOKIE_STATE || e === AUTH_TAG_LOGIN_COOKIE || e === AUTH_TAG_LOGIN_BEARER);
|
|
8825
|
-
if (explicit.length === 2 && explicit.includes(AUTH_TAG_STANDING) && loginCompose.length === 1) {
|
|
8826
|
-
const composedKeys = [...TEST_USER_ENV_KEYS, API_AUTH_HEADERS_ENV_KEY];
|
|
8827
|
-
switch (loginCompose[0]) {
|
|
8828
|
-
case AUTH_TAG_COOKIE_STATE:
|
|
8829
|
-
return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
8830
|
-
case AUTH_TAG_LOGIN_COOKIE:
|
|
8831
|
-
return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
8832
|
-
case AUTH_TAG_LOGIN_BEARER:
|
|
8833
|
-
return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
8834
|
-
}
|
|
8835
|
-
}
|
|
8836
|
-
if (explicit.length > 1) {
|
|
8837
|
-
return invalidPlan(`conflicting auth-type tags: ${explicit.join(", ")}`);
|
|
8838
|
-
}
|
|
8839
|
-
switch (explicit[0]) {
|
|
8840
|
-
case AUTH_TAG_UNSUPPORTED:
|
|
9320
|
+
for (const text of sources) {
|
|
9321
|
+
if (!text)
|
|
9322
|
+
continue;
|
|
9323
|
+
for (const sig of AUTH_BLOCK_SIGNATURES) {
|
|
9324
|
+
if (sig.re.test(text)) {
|
|
8841
9325
|
return {
|
|
8842
|
-
|
|
9326
|
+
detected: true,
|
|
9327
|
+
reason: sig.reason,
|
|
9328
|
+
evidence: [`login-block signature "${sig.reason}" in: "${text.slice(0, 140).replace(/\s+/g, " ").trim()}"`]
|
|
8843
9329
|
};
|
|
8844
|
-
|
|
8845
|
-
return explicitPlan(AUTH_TAG_NONE, "none", "none", { cookie: false, standingMode: "none", requiredEnvKeys: [] });
|
|
8846
|
-
case AUTH_TAG_STANDING:
|
|
8847
|
-
return explicitPlan(AUTH_TAG_STANDING, "standing-secret", "standing-header", { cookie: false, standingMode: "required", requiredEnvKeys: [API_AUTH_HEADERS_ENV_KEY] });
|
|
8848
|
-
case AUTH_TAG_LOGIN_BEARER:
|
|
8849
|
-
return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "none", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
8850
|
-
case AUTH_TAG_LOGIN_COOKIE:
|
|
8851
|
-
return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
8852
|
-
case AUTH_TAG_COOKIE_STATE:
|
|
8853
|
-
return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
9330
|
+
}
|
|
8854
9331
|
}
|
|
8855
9332
|
}
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
const
|
|
8860
|
-
|
|
9333
|
+
return NO_DETECTION;
|
|
9334
|
+
}
|
|
9335
|
+
function detectApiColdSessionBlock(input) {
|
|
9336
|
+
const tags = input.tags ?? [];
|
|
9337
|
+
if (!tags.includes("@api"))
|
|
9338
|
+
return NO_DETECTION;
|
|
9339
|
+
const cookieMissing = input.authStateExpected && !input.authStateInjected;
|
|
9340
|
+
const standingMissing = !!input.needsStandingHeaders && !input.standingHeadersInjected;
|
|
9341
|
+
if (!cookieMissing && !standingMissing)
|
|
9342
|
+
return NO_DETECTION;
|
|
9343
|
+
const has401Status = (input.apiCalls ?? []).some((c) => c && (c.status === 401 || c.status === 403));
|
|
9344
|
+
const texts = [];
|
|
9345
|
+
if (input.error)
|
|
9346
|
+
texts.push(input.error);
|
|
9347
|
+
if (input.logs)
|
|
9348
|
+
texts.push(input.logs);
|
|
9349
|
+
for (const sr of input.stepResults ?? [])
|
|
9350
|
+
if (sr.error)
|
|
9351
|
+
texts.push(sr.error);
|
|
9352
|
+
const has401Text = texts.some((t) => UNAUTHORIZED_TEXT_RE.test(t));
|
|
9353
|
+
if (!has401Status && !has401Text)
|
|
9354
|
+
return NO_DETECTION;
|
|
9355
|
+
const missingAuth = cookieMissing ? "cold injected auth session (401/403 with no storageState)" : "missing standing auth header (401/403 with no API_AUTH_HEADERS installed)";
|
|
9356
|
+
const injectedNoun = cookieMissing ? "injected session was absent/expired" : "standing auth header was not installed";
|
|
8861
9357
|
return {
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
needsStandingHeaders: false,
|
|
8868
|
-
// A legacy bare-@api test keeps injecting standing headers IF the project configured them
|
|
8869
|
-
// (preserving today's behavior) — but does not require them. Non-@api legacy tests never inject.
|
|
8870
|
-
standingHeadersMode: isApi ? "legacy-if-configured" : "none",
|
|
8871
|
-
needsLoginCreds,
|
|
8872
|
-
requiredEnvKeys: needsLoginCreds ? [...TEST_USER_ENV_KEYS] : []
|
|
9358
|
+
detected: true,
|
|
9359
|
+
reason: missingAuth,
|
|
9360
|
+
evidence: [
|
|
9361
|
+
has401Status ? `a captured @api call returned 401/403 while the ${injectedNoun}` : `a 401/403 signal in the run error/logs while the ${injectedNoun}`
|
|
9362
|
+
]
|
|
8873
9363
|
};
|
|
8874
9364
|
}
|
|
8875
|
-
function
|
|
8876
|
-
|
|
8877
|
-
const explicit = explicitAuthTypeTags(t);
|
|
8878
|
-
return explicit.length === 1 ? explicit[0] : void 0;
|
|
9365
|
+
function coldSessionConfigMessage(context, reason, evidence) {
|
|
9366
|
+
return `CONFIG_ISSUE: ${context} without valid injected auth (${reason}) \u2014 no session cookie was injected this run. Ensure the project login (auth-setup recipe / prewarm) is configured so the shared session jar warms (a project with no login: set TEST_USER_EMAIL and TEST_USER_PASSWORD in Test Data \u2192 Test Variables so a session can be captured); for endpoints that authenticate with a standing API key or bearer header instead, set them in the project's API_AUTH_HEADERS variable (injected as request headers). Evidence: ${evidence.join("; ")}`;
|
|
8879
9367
|
}
|
|
8880
|
-
var
|
|
8881
|
-
var
|
|
8882
|
-
"../shared/dist/auth-
|
|
9368
|
+
var NO_DETECTION, AUTHED_TAGS, LOGIN_CTX, AUTH_BLOCK_SIGNATURES, INTENTIONAL_NEGATIVE_AUTH, ECHO_LINE_RE, UNAUTHORIZED_TEXT_RE;
|
|
9369
|
+
var init_auth_block_detector = __esm({
|
|
9370
|
+
"../shared/dist/auth-block-detector.js"() {
|
|
8883
9371
|
"use strict";
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
9372
|
+
NO_DETECTION = { detected: false, reason: "", evidence: [] };
|
|
9373
|
+
AUTHED_TAGS = ["@needs-auth", "@inline-login"];
|
|
9374
|
+
LOGIN_CTX = "\\b(?:log\\s*in|login|logging\\s+in|sign[\\s-]?in|signing\\s+in|continue)\\b";
|
|
9375
|
+
AUTH_BLOCK_SIGNATURES = [
|
|
9376
|
+
{ reason: "incorrect password", re: /\b(incorrect|invalid|wrong|bad)\s+password\b/i },
|
|
9377
|
+
{ reason: "password is incorrect", re: /\bpassword\s+(is\s+)?(incorrect|invalid|wrong)\b/i },
|
|
9378
|
+
{ reason: "invalid credentials", re: /\b(invalid|incorrect|wrong|bad)\s+(login\s+)?credentials\b/i },
|
|
9379
|
+
{ reason: "credentials do not match", re: /\bcredentials\b[^\n]{0,40}\b(do(?:es)?\s*n['’]?t|do\s+not)\s+match\b/i },
|
|
9380
|
+
{ reason: "incorrect email or password", re: /\b(incorrect|invalid|wrong)\s+(email|username)\s+or\s+password\b/i },
|
|
9381
|
+
// Require an EXPLICIT negative (codex #21): the old `(not\s+)?(confirmed|verified)` made the negation
|
|
9382
|
+
// optional, so a benign "Email verified" success message / feature assertion matched and hid a real
|
|
9383
|
+
// feature failure as a CONFIG_ISSUE. Only "email not (yet) confirmed/verified" or "unconfirmed/unverified".
|
|
9384
|
+
{ reason: "email not confirmed", re: /\b(e-?mail)\b[^\n]{0,30}\b(not\s+(?:yet\s+)?(?:confirmed|verified)|unconfirmed|unverified)\b/i },
|
|
9385
|
+
{ reason: "please confirm your email", re: /\bplease\s+(confirm|verify)\s+your\s+(e-?mail)\b/i },
|
|
9386
|
+
// The bare "confirm/verify your email" and "account not found" phrases are ALSO ordinary feature copy
|
|
9387
|
+
// ("Verify your email banner", a 404-style "Account not found" page), so they only count with a
|
|
9388
|
+
// login/sign-in context on the SAME line (either side). "Please confirm/verify your email" above
|
|
9389
|
+
// is already an explicit prompt and stays unconditional.
|
|
9390
|
+
{ reason: "confirm your email to continue", re: new RegExp(`${LOGIN_CTX}[^\\n]{0,80}\\bconfirm\\s+your\\s+e-?mail\\b|\\bconfirm\\s+your\\s+e-?mail\\b[^\\n]{0,80}${LOGIN_CTX}`, "i") },
|
|
9391
|
+
{ reason: "verify your email to continue", re: new RegExp(`${LOGIN_CTX}[^\\n]{0,80}\\bverify\\s+your\\s+e-?mail\\b|\\bverify\\s+your\\s+e-?mail\\b[^\\n]{0,80}${LOGIN_CTX}`, "i") },
|
|
9392
|
+
{
|
|
9393
|
+
reason: "account not found",
|
|
9394
|
+
re: new RegExp(`${LOGIN_CTX}[^\\n]{0,80}\\baccount\\b[^\\n]{0,20}\\b(?:not\\s+found|does(?:n['\u2019]t| not)\\s+exist)\\b|\\baccount\\b[^\\n]{0,20}\\b(?:not\\s+found|does(?:n['\u2019]t| not)\\s+exist)\\b[^\\n]{0,80}${LOGIN_CTX}`, "i")
|
|
9395
|
+
},
|
|
9396
|
+
{ reason: "authentication failed", re: /\bauthentication\s+failed\b/i },
|
|
9397
|
+
{ reason: "login failed", re: /\b(login|log\s*in|sign[\s-]?in)\s+failed\b/i }
|
|
8897
9398
|
];
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
"
|
|
8902
|
-
"
|
|
8903
|
-
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
9399
|
+
INTENTIONAL_NEGATIVE_AUTH = [
|
|
9400
|
+
/\b(invalid|incorrect|wrong|bad)\s+(password|credentials|email|login)\b/i,
|
|
9401
|
+
/\b(negative|unhappy)\s+(login|auth|sign[\s-]?in|path|case|flow|test)\b/i,
|
|
9402
|
+
// "should fail" et al must be TIED to an auth noun (codex #21): a bare `should\s+(fail|reject|deny)`
|
|
9403
|
+
// exempted benign names like "Export should fail gracefully", silently disabling the anti-lockout
|
|
9404
|
+
// protection for them. Require a login/auth noun on either side of the "should fail / fail to log in".
|
|
9405
|
+
/\b(login|log\s*in|sign[\s-]?in|auth\w*|credential|password)\b[^\n]{0,40}\bshould\s+(fail|reject|deny)\b/i,
|
|
9406
|
+
/\bshould\s+(fail|reject|deny|not\s+log(?:\s*in)?)\b[^\n]{0,40}\b(login|log\s*in|sign[\s-]?in|auth\w*|credential|password|unauthenticated)\b/i,
|
|
9407
|
+
/\bfailed?\s+login\b/i,
|
|
9408
|
+
/\blogin\s+(error|failure|rejected?)\b/i,
|
|
9409
|
+
/\b(reject|deny|denied|block(ed)?)\b[^\n]{0,20}\b(login|sign[\s-]?in|auth|credential|password)\b/i
|
|
9410
|
+
];
|
|
9411
|
+
ECHO_LINE_RE = /^\s*(?:(?:📋|🎭|🌐|⚙️?)|(?:stderr:\s*|stdout:\s*)?Locator:)/u;
|
|
9412
|
+
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;
|
|
9413
|
+
}
|
|
9414
|
+
});
|
|
9415
|
+
|
|
9416
|
+
// ../shared/dist/auth-session-stamp.js
|
|
9417
|
+
function parseAuthSessionStamp(value) {
|
|
9418
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
9419
|
+
return null;
|
|
9420
|
+
const { shared, role, generation } = value;
|
|
9421
|
+
if (shared !== true || role !== "producer" && role !== "follower")
|
|
9422
|
+
return null;
|
|
9423
|
+
return typeof generation === "string" && AUTH_SESSION_GENERATION_RE.test(generation) ? { shared: true, role, generation } : { shared: true, role };
|
|
9424
|
+
}
|
|
9425
|
+
var AUTH_SESSION_GENERATION_RE;
|
|
9426
|
+
var init_auth_session_stamp = __esm({
|
|
9427
|
+
"../shared/dist/auth-session-stamp.js"() {
|
|
9428
|
+
"use strict";
|
|
9429
|
+
AUTH_SESSION_GENERATION_RE = /^[0-9a-z]{1,16}$/;
|
|
8907
9430
|
}
|
|
8908
9431
|
});
|
|
8909
9432
|
|
|
@@ -9084,11 +9607,48 @@ function storageStateExpiredBySkew(stateJson, nowMs, skewMs = AUTH_STATE_EXPIRY_
|
|
|
9084
9607
|
const liveSession = tokens2.some((t) => t.expiryMs > deadline && t.sessionClass);
|
|
9085
9608
|
return !liveSession;
|
|
9086
9609
|
}
|
|
9087
|
-
var AUTH_STATE_EXPIRY_SKEW_MS;
|
|
9088
|
-
var init_auth_state_freshness = __esm({
|
|
9089
|
-
"../shared/dist/auth-state-freshness.js"() {
|
|
9610
|
+
var AUTH_STATE_EXPIRY_SKEW_MS;
|
|
9611
|
+
var init_auth_state_freshness = __esm({
|
|
9612
|
+
"../shared/dist/auth-state-freshness.js"() {
|
|
9613
|
+
"use strict";
|
|
9614
|
+
AUTH_STATE_EXPIRY_SKEW_MS = 3e4;
|
|
9615
|
+
}
|
|
9616
|
+
});
|
|
9617
|
+
|
|
9618
|
+
// ../shared/dist/skip-coverage-detector.js
|
|
9619
|
+
function detectSkippedApiCoverage(steps) {
|
|
9620
|
+
const allSkipped = steps.length > 0 && steps.every((s) => s.status === "skipped");
|
|
9621
|
+
if (allSkipped) {
|
|
9622
|
+
if (steps.every((s) => s.skipKind === "fixme")) {
|
|
9623
|
+
return {
|
|
9624
|
+
detected: false,
|
|
9625
|
+
logLine: "All @api tests skipped, but EVERY skip is a deliberate fixme (intentional N/A \u2014 e.g. a UI-only redirect login) \u2014 NOT relabelling CONFIG_ISSUE"
|
|
9626
|
+
};
|
|
9627
|
+
}
|
|
9628
|
+
return {
|
|
9629
|
+
detected: true,
|
|
9630
|
+
error: (
|
|
9631
|
+
// Name BOTH auth mechanisms (item 5d): the all-skipped copy used to say only "refresh the auth
|
|
9632
|
+
// state", which misdirects a @auth-standing operator (whose real gap is a missing API_AUTH_HEADERS
|
|
9633
|
+
// key, not a cold cookie session) to warm a login that was never the problem.
|
|
9634
|
+
"CONFIG_ISSUE: Authenticated @api coverage was NOT exercised \u2014 every test skipped (cold injected session, a missing standing auth header, or an unresolved fixme). Provide the auth the endpoints need \u2014 warm the project login/session cookie (auth-setup recipe / prewarm), or set a standing API key/bearer in the project's API_AUTH_HEADERS variable \u2014 so the assertions actually run."
|
|
9635
|
+
),
|
|
9636
|
+
logLine: "All @api tests skipped \u2014 posting ERROR/CONFIG_ISSUE (not PASSED) so zero authenticated coverage is visible, not silent green"
|
|
9637
|
+
};
|
|
9638
|
+
}
|
|
9639
|
+
const runtimeSkipped = steps.filter((s) => s.status === "skipped" && s.skipKind === "skip");
|
|
9640
|
+
if (runtimeSkipped.length === 0)
|
|
9641
|
+
return { detected: false };
|
|
9642
|
+
const reasons = [...new Set(runtimeSkipped.map((s) => `"${s.title ?? "test"}"${s.skipReason ? ` (${s.skipReason})` : ""}`))].join("; ");
|
|
9643
|
+
return {
|
|
9644
|
+
detected: true,
|
|
9645
|
+
error: `CONFIG_ISSUE: ${runtimeSkipped.length} of ${steps.length} @api tests skipped at runtime \u2014 that coverage was NOT exercised. Skipped: ${reasons}`,
|
|
9646
|
+
logLine: `${runtimeSkipped.length}/${steps.length} @api tests skipped at runtime \u2014 posting ERROR/CONFIG_ISSUE (not PASSED) so the coverage hole is visible, not silent green`
|
|
9647
|
+
};
|
|
9648
|
+
}
|
|
9649
|
+
var init_skip_coverage_detector = __esm({
|
|
9650
|
+
"../shared/dist/skip-coverage-detector.js"() {
|
|
9090
9651
|
"use strict";
|
|
9091
|
-
AUTH_STATE_EXPIRY_SKEW_MS = 3e4;
|
|
9092
9652
|
}
|
|
9093
9653
|
});
|
|
9094
9654
|
|
|
@@ -9783,6 +10343,77 @@ var init_deepgram_speech = __esm({
|
|
|
9783
10343
|
}
|
|
9784
10344
|
});
|
|
9785
10345
|
|
|
10346
|
+
// ../shared/dist/feature-flags.js
|
|
10347
|
+
function isSharedAuthInjectEnabled(value = process.env.SHARED_AUTH_INJECT) {
|
|
10348
|
+
return value?.trim().toLowerCase() !== "false";
|
|
10349
|
+
}
|
|
10350
|
+
function isAuthReadinessGuardEnabled(value = process.env.AUTH_READINESS_GUARD) {
|
|
10351
|
+
return value?.trim().toLowerCase() !== "false";
|
|
10352
|
+
}
|
|
10353
|
+
function visualBaselinesEnabled(projectId, raw = process.env.VISUAL_BASELINES_PROJECTS) {
|
|
10354
|
+
if (typeof projectId !== "string" || projectId.trim() === "")
|
|
10355
|
+
return false;
|
|
10356
|
+
const listed = (raw ?? "").split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
10357
|
+
return listed.includes("*") || listed.includes(projectId);
|
|
10358
|
+
}
|
|
10359
|
+
var init_feature_flags = __esm({
|
|
10360
|
+
"../shared/dist/feature-flags.js"() {
|
|
10361
|
+
"use strict";
|
|
10362
|
+
}
|
|
10363
|
+
});
|
|
10364
|
+
|
|
10365
|
+
// ../shared/dist/visual-compare-limits.js
|
|
10366
|
+
function isSafeMaskSelector(selector) {
|
|
10367
|
+
if (typeof selector !== "string")
|
|
10368
|
+
return false;
|
|
10369
|
+
if (selector.trim().length === 0)
|
|
10370
|
+
return false;
|
|
10371
|
+
if (selector.length > VISUAL_MASK_SELECTOR_MAX_LENGTH)
|
|
10372
|
+
return false;
|
|
10373
|
+
return !VISUAL_MASK_SELECTOR_FORBIDDEN.test(selector);
|
|
10374
|
+
}
|
|
10375
|
+
var VISUAL_MASK_SELECTORS_MAX, VISUAL_MASK_SELECTOR_MAX_LENGTH, VISUAL_MASK_SELECTOR_FORBIDDEN;
|
|
10376
|
+
var init_visual_compare_limits = __esm({
|
|
10377
|
+
"../shared/dist/visual-compare-limits.js"() {
|
|
10378
|
+
"use strict";
|
|
10379
|
+
VISUAL_MASK_SELECTORS_MAX = 50;
|
|
10380
|
+
VISUAL_MASK_SELECTOR_MAX_LENGTH = 300;
|
|
10381
|
+
VISUAL_MASK_SELECTOR_FORBIDDEN = /[{};@<\r\n]/;
|
|
10382
|
+
}
|
|
10383
|
+
});
|
|
10384
|
+
|
|
10385
|
+
// ../shared/dist/visual-baseline-identity.js
|
|
10386
|
+
function isBrowserVersion(value) {
|
|
10387
|
+
return typeof value === "string" && value.length <= 32 && BROWSER_VERSION.test(value);
|
|
10388
|
+
}
|
|
10389
|
+
function renderIdentity(flavour, version, image) {
|
|
10390
|
+
if (!/^[a-z][a-z-]{0,31}$/.test(flavour) || !/^[a-z][a-z-]{0,31}$/.test(image) || !isBrowserVersion(version))
|
|
10391
|
+
return null;
|
|
10392
|
+
const identity = `${flavour}/${version}/${image}`;
|
|
10393
|
+
return identity.length <= RENDER_IDENTITY_MAX_LENGTH ? identity : null;
|
|
10394
|
+
}
|
|
10395
|
+
function v1RenderIdentity(version) {
|
|
10396
|
+
return renderIdentity(VISUAL_V1_IDENTITY.flavour, version, VISUAL_V1_IDENTITY.image);
|
|
10397
|
+
}
|
|
10398
|
+
function parseRenderIdentity(value) {
|
|
10399
|
+
if (typeof value !== "string" || value.length > RENDER_IDENTITY_MAX_LENGTH)
|
|
10400
|
+
return null;
|
|
10401
|
+
const parts = value.split("/");
|
|
10402
|
+
if (parts.length !== 3)
|
|
10403
|
+
return null;
|
|
10404
|
+
const [flavour, version, image] = parts;
|
|
10405
|
+
return renderIdentity(flavour, version, image) === value ? { flavour, version, image } : null;
|
|
10406
|
+
}
|
|
10407
|
+
var VISUAL_V1_IDENTITY, RENDER_IDENTITY_MAX_LENGTH, BROWSER_VERSION;
|
|
10408
|
+
var init_visual_baseline_identity = __esm({
|
|
10409
|
+
"../shared/dist/visual-baseline-identity.js"() {
|
|
10410
|
+
"use strict";
|
|
10411
|
+
VISUAL_V1_IDENTITY = { platform: "linux", image: "runner-fast", flavour: "headless-shell" };
|
|
10412
|
+
RENDER_IDENTITY_MAX_LENGTH = 64;
|
|
10413
|
+
BROWSER_VERSION = /^\d{1,4}(\.\d{1,6}){0,3}$/;
|
|
10414
|
+
}
|
|
10415
|
+
});
|
|
10416
|
+
|
|
9786
10417
|
// ../shared/dist/analytics-url.js
|
|
9787
10418
|
function isSensitiveQueryKey(key) {
|
|
9788
10419
|
return SENSITIVE_QUERY_KEY_RE.test(key);
|
|
@@ -10085,7 +10716,7 @@ var init_agentic_cli_runtime = __esm({
|
|
|
10085
10716
|
GROK_CLI_DEFAULT_MODEL = "grok-4.7";
|
|
10086
10717
|
GROK_CLI_DEFAULT_REASONING_EFFORT = "high";
|
|
10087
10718
|
CLAUDE_CLI_DEFAULT_MODEL = "claude-sonnet-5-5";
|
|
10088
|
-
CLAUDE_CLI_DEFAULT_EFFORT = "
|
|
10719
|
+
CLAUDE_CLI_DEFAULT_EFFORT = "high";
|
|
10089
10720
|
MUSE_CLI_DEFAULT_MODEL = "muse-spark-1.3-contributor";
|
|
10090
10721
|
MUSE_CLI_DEFAULT_EFFORT = "high";
|
|
10091
10722
|
CODEX_CLI_DEFAULT_MODEL = "gpt-6.1-sol";
|
|
@@ -10140,6 +10771,17 @@ function openCodeGoWireModel(id) {
|
|
|
10140
10771
|
const trimmed = id?.trim() ?? "";
|
|
10141
10772
|
return isOpenCodeGoModelId(trimmed) ? trimmed.slice(OPENCODE_GO_PREFIX.length) : null;
|
|
10142
10773
|
}
|
|
10774
|
+
function isZaiModelId(id) {
|
|
10775
|
+
const trimmed = id?.trim() ?? "";
|
|
10776
|
+
return ZAI_MODEL_PATTERN.test(trimmed) && !trimmed.includes("..");
|
|
10777
|
+
}
|
|
10778
|
+
function hasZaiPrefix(id) {
|
|
10779
|
+
return (id?.trim() ?? "").startsWith(ZAI_PREFIX);
|
|
10780
|
+
}
|
|
10781
|
+
function zaiWireModel(id) {
|
|
10782
|
+
const trimmed = id?.trim() ?? "";
|
|
10783
|
+
return isZaiModelId(trimmed) ? trimmed.slice(ZAI_PREFIX.length) : null;
|
|
10784
|
+
}
|
|
10143
10785
|
function isEngineId(value) {
|
|
10144
10786
|
return CLI_ENGINE_MODEL_IDS.includes(value);
|
|
10145
10787
|
}
|
|
@@ -10236,7 +10878,7 @@ function museCliContributorMode(env = process.env) {
|
|
|
10236
10878
|
const raw = (env.MUSE_CLI_CONTRIBUTOR_MODE ?? "").trim().toLowerCase();
|
|
10237
10879
|
return raw === "off" || raw === "false" || raw === "0" || raw === "no" ? "off" : "on";
|
|
10238
10880
|
}
|
|
10239
|
-
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;
|
|
10881
|
+
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;
|
|
10240
10882
|
var init_engine_spec = __esm({
|
|
10241
10883
|
"../shared/dist/engine-spec.js"() {
|
|
10242
10884
|
"use strict";
|
|
@@ -10296,6 +10938,8 @@ var init_engine_spec = __esm({
|
|
|
10296
10938
|
OPENROUTER_MODEL_PATTERN = /^openrouter:[a-z0-9][a-z0-9._-]{0,40}\/[A-Za-z0-9._:-]{1,100}$/;
|
|
10297
10939
|
OPENCODE_GO_PREFIX = "opencode-go:";
|
|
10298
10940
|
OPENCODE_GO_MODEL_PATTERN = /^opencode-go:[a-z0-9][a-z0-9._-]{0,100}$/;
|
|
10941
|
+
ZAI_PREFIX = "zai:";
|
|
10942
|
+
ZAI_MODEL_PATTERN = /^zai:glm-[a-z0-9][a-z0-9._-]{0,60}$/;
|
|
10299
10943
|
}
|
|
10300
10944
|
});
|
|
10301
10945
|
|
|
@@ -10580,6 +11224,8 @@ var init_api_model_catalog = __esm({
|
|
|
10580
11224
|
authCaptureModel: "Login capture",
|
|
10581
11225
|
plannerModel: "Scenario Planner",
|
|
10582
11226
|
plannerGateModel: "Planner Gate",
|
|
11227
|
+
pwAgentsHealerModel: "Test Healer (Playwright agents)",
|
|
11228
|
+
pwAgentsReviewModel: "Plan Review (Playwright agents)",
|
|
10583
11229
|
generationModel: "Code Generation",
|
|
10584
11230
|
reviewModel: "Code Review",
|
|
10585
11231
|
apiTestModel: "API Test Generator",
|
|
@@ -10604,11 +11250,6 @@ var init_api_model_catalog = __esm({
|
|
|
10604
11250
|
});
|
|
10605
11251
|
|
|
10606
11252
|
// ../shared/dist/provider-chains.js
|
|
10607
|
-
function isClaudeModel(id) {
|
|
10608
|
-
if (/claude|anthropic/i.test(id))
|
|
10609
|
-
return true;
|
|
10610
|
-
return API_MODEL_CATALOG.some((m) => m.provider === "anthropic" && (m.id === id || m.apiModel === id));
|
|
10611
|
-
}
|
|
10612
11253
|
function chainKey(modelId) {
|
|
10613
11254
|
const id = canonicalModelId(modelId);
|
|
10614
11255
|
if (!id)
|
|
@@ -10616,35 +11257,334 @@ function chainKey(modelId) {
|
|
|
10616
11257
|
const entry = API_MODEL_CATALOG.find((m) => m.id === id || m.apiModel === id);
|
|
10617
11258
|
return entry?.id ?? id;
|
|
10618
11259
|
}
|
|
11260
|
+
function has(map, key) {
|
|
11261
|
+
return Object.prototype.hasOwnProperty.call(map, key);
|
|
11262
|
+
}
|
|
10619
11263
|
function providerChainFor(modelId) {
|
|
10620
11264
|
const key = chainKey(modelId);
|
|
10621
|
-
if (
|
|
10622
|
-
return [];
|
|
10623
|
-
return
|
|
11265
|
+
if (key && has(_overrideHops, key))
|
|
11266
|
+
return _overrideHops[key];
|
|
11267
|
+
return key && has(PROVIDER_CHAINS, key) ? PROVIDER_CHAINS[key] : [];
|
|
11268
|
+
}
|
|
11269
|
+
function providerChainLeadsFor(modelId) {
|
|
11270
|
+
const key = chainKey(modelId);
|
|
11271
|
+
if (key && has(_overrideLeads, key))
|
|
11272
|
+
return _overrideLeads[key];
|
|
11273
|
+
return key && has(PROVIDER_CHAIN_LEADS, key) ? PROVIDER_CHAIN_LEADS[key] : [];
|
|
11274
|
+
}
|
|
11275
|
+
function allChains() {
|
|
11276
|
+
const ordered = (map, override) => [...Object.entries(map).filter(([key]) => !has(override, key)), ...Object.entries(override)];
|
|
11277
|
+
return [...ordered(PROVIDER_CHAINS, _overrideHops), ...ordered(PROVIDER_CHAIN_LEADS, _overrideLeads)];
|
|
10624
11278
|
}
|
|
10625
11279
|
function isProviderChainHopId(modelId) {
|
|
10626
11280
|
const id = modelId ?? "";
|
|
10627
|
-
return id !== "" &&
|
|
11281
|
+
return id !== "" && allChains().some(([, hops]) => hops.includes(id));
|
|
10628
11282
|
}
|
|
10629
11283
|
function providerChainPrimariesForHop(hopId) {
|
|
10630
11284
|
const id = hopId ?? "";
|
|
10631
11285
|
if (!id)
|
|
10632
11286
|
return [];
|
|
10633
|
-
return
|
|
11287
|
+
return allChains().filter(([, hops]) => hops.includes(id)).map(([primary]) => primary);
|
|
11288
|
+
}
|
|
11289
|
+
function providerChainHostOf(value, modelValue) {
|
|
11290
|
+
if (value === modelValue)
|
|
11291
|
+
return "own";
|
|
11292
|
+
if (hasZaiPrefix(value))
|
|
11293
|
+
return "zai";
|
|
11294
|
+
if (hasOpenCodeGoPrefix(value))
|
|
11295
|
+
return "opencode-go";
|
|
11296
|
+
if (hasOpenRouterPrefix(value))
|
|
11297
|
+
return "openrouter";
|
|
11298
|
+
return "own";
|
|
11299
|
+
}
|
|
11300
|
+
function providerChainHostProvider(value) {
|
|
11301
|
+
if (typeof value !== "string" || !value)
|
|
11302
|
+
return null;
|
|
11303
|
+
if (hasOpenRouterPrefix(value))
|
|
11304
|
+
return "openrouter";
|
|
11305
|
+
if (hasOpenCodeGoPrefix(value))
|
|
11306
|
+
return "opencode-go";
|
|
11307
|
+
if (hasZaiPrefix(value))
|
|
11308
|
+
return "zai";
|
|
11309
|
+
const key = chainKey(value);
|
|
11310
|
+
return API_MODEL_CATALOG.find((m) => m.id === key)?.provider ?? GEMINI_ASSISTANT_MODELS.find((m) => m.id === key)?.provider ?? null;
|
|
11311
|
+
}
|
|
11312
|
+
function providerHostPlan(modelId) {
|
|
11313
|
+
const key = chainKey(modelId);
|
|
11314
|
+
if (!key)
|
|
11315
|
+
return [];
|
|
11316
|
+
const own = { host: hasOpenRouterPrefix(key) ? "openrouter" : "own", value: key };
|
|
11317
|
+
const steps = [
|
|
11318
|
+
...providerChainLeadsFor(key).map((value) => ({ host: providerChainHostOf(value, key), value })),
|
|
11319
|
+
own,
|
|
11320
|
+
...providerChainFor(key).map((value) => ({ host: providerChainHostOf(value, key), value }))
|
|
11321
|
+
];
|
|
11322
|
+
const notOffered = Object.prototype.hasOwnProperty.call(PROVIDER_HOST_NOT_OFFERED, key) ? PROVIDER_HOST_NOT_OFFERED[key] : {};
|
|
11323
|
+
for (const [host, reason] of Object.entries(notOffered)) {
|
|
11324
|
+
const at = steps.findIndex((step) => step === own) + 1;
|
|
11325
|
+
steps.splice(at, 0, { host, value: null, notOffered: reason });
|
|
11326
|
+
}
|
|
11327
|
+
return steps;
|
|
11328
|
+
}
|
|
11329
|
+
function providerChainModelKey(modelId) {
|
|
11330
|
+
return chainKey(modelId);
|
|
11331
|
+
}
|
|
11332
|
+
function hasProviderChainLeadsByDefault(modelId) {
|
|
11333
|
+
const key = chainKey(modelId);
|
|
11334
|
+
return key !== null && has(PROVIDER_CHAIN_LEADS, key);
|
|
11335
|
+
}
|
|
11336
|
+
function isChainablePrimary(id) {
|
|
11337
|
+
return typeof id === "string" && id !== "" && (has(PROVIDER_CHAINS, id) || has(PROVIDER_CHAIN_LEADS, id));
|
|
11338
|
+
}
|
|
11339
|
+
function defaultProviderChainEntries(primary) {
|
|
11340
|
+
if (!isChainablePrimary(primary))
|
|
11341
|
+
return [];
|
|
11342
|
+
const leads = has(PROVIDER_CHAIN_LEADS, primary) ? PROVIDER_CHAIN_LEADS[primary] : [];
|
|
11343
|
+
const hops = has(PROVIDER_CHAINS, primary) ? PROVIDER_CHAINS[primary] : [];
|
|
11344
|
+
return [...leads, primary, ...hops].map((hop) => ({ hop, enabled: true }));
|
|
11345
|
+
}
|
|
11346
|
+
function allowedProviderChainHosts(primary) {
|
|
11347
|
+
return defaultProviderChainEntries(primary).map((entry) => entry.hop);
|
|
11348
|
+
}
|
|
11349
|
+
function isPlainObject2(value) {
|
|
11350
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
11351
|
+
}
|
|
11352
|
+
function validateProviderChainEntries(primary, entries) {
|
|
11353
|
+
if (!isChainablePrimary(primary))
|
|
11354
|
+
return `${String(primary).slice(0, 80)} has no provider chain to order`;
|
|
11355
|
+
if (!Array.isArray(entries))
|
|
11356
|
+
return `${primary}: the chain must be a list`;
|
|
11357
|
+
if (entries.length > PROVIDER_CHAIN_MAX_ENTRIES)
|
|
11358
|
+
return `${primary}: at most ${PROVIDER_CHAIN_MAX_ENTRIES} hosts`;
|
|
11359
|
+
const allowed = allowedProviderChainHosts(primary);
|
|
11360
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11361
|
+
for (const entry of entries) {
|
|
11362
|
+
if (!isPlainObject2(entry))
|
|
11363
|
+
return `${primary}: each host must be { hop, enabled }`;
|
|
11364
|
+
const keys = Object.keys(entry);
|
|
11365
|
+
if (keys.length !== 2 || !keys.includes("hop") || !keys.includes("enabled"))
|
|
11366
|
+
return `${primary}: each host must be { hop, enabled } and nothing else`;
|
|
11367
|
+
const { hop, enabled } = entry;
|
|
11368
|
+
if (typeof enabled !== "boolean")
|
|
11369
|
+
return `${primary}: enabled must be true or false`;
|
|
11370
|
+
if (typeof hop !== "string" || !hop)
|
|
11371
|
+
return `${primary}: hop must be a model value`;
|
|
11372
|
+
if (!allowed.includes(hop))
|
|
11373
|
+
return `${primary}: ${hop.slice(0, 80)} is not a host of this model`;
|
|
11374
|
+
if (seen.has(hop))
|
|
11375
|
+
return `${primary}: ${hop} is listed twice`;
|
|
11376
|
+
if (hop === primary && !enabled)
|
|
11377
|
+
return `${primary}: the model's own value stays on (pick another model on AI Models instead)`;
|
|
11378
|
+
seen.add(hop);
|
|
11379
|
+
}
|
|
11380
|
+
if (!seen.has(primary))
|
|
11381
|
+
return `${primary}: list the model's own value once, where its own host sits`;
|
|
11382
|
+
return null;
|
|
11383
|
+
}
|
|
11384
|
+
function decodeProviderChains(raw) {
|
|
11385
|
+
let value = raw;
|
|
11386
|
+
if (typeof raw === "string") {
|
|
11387
|
+
if (raw.length > PROVIDER_CHAINS_SETTING_MAX_CHARS)
|
|
11388
|
+
return { ok: false, error: `AI_PROVIDER_CHAINS is too long (at most ${PROVIDER_CHAINS_SETTING_MAX_CHARS} characters)` };
|
|
11389
|
+
try {
|
|
11390
|
+
value = JSON.parse(raw);
|
|
11391
|
+
} catch {
|
|
11392
|
+
return { ok: false, error: "AI_PROVIDER_CHAINS must be valid JSON" };
|
|
11393
|
+
}
|
|
11394
|
+
} else if (JSON.stringify(raw ?? null).length > PROVIDER_CHAINS_SETTING_MAX_CHARS) {
|
|
11395
|
+
return { ok: false, error: `AI_PROVIDER_CHAINS is too long (at most ${PROVIDER_CHAINS_SETTING_MAX_CHARS} characters)` };
|
|
11396
|
+
}
|
|
11397
|
+
if (!isPlainObject2(value))
|
|
11398
|
+
return { ok: false, error: 'AI_PROVIDER_CHAINS must be an object: { "<model>": [ { "hop": "<model value>", "enabled": true } ] }' };
|
|
11399
|
+
if (Object.keys(value).length > PROVIDER_CHAIN_MAX_PRIMARIES)
|
|
11400
|
+
return { ok: false, error: `AI_PROVIDER_CHAINS names at most ${PROVIDER_CHAIN_MAX_PRIMARIES} models` };
|
|
11401
|
+
return { ok: true, value };
|
|
11402
|
+
}
|
|
11403
|
+
function checkedProviderChains(value) {
|
|
11404
|
+
const out = {};
|
|
11405
|
+
for (const [primary, entries] of Object.entries(value)) {
|
|
11406
|
+
const problem = validateProviderChainEntries(primary, entries);
|
|
11407
|
+
if (problem)
|
|
11408
|
+
return { ok: false, error: problem };
|
|
11409
|
+
out[primary] = entries.map(({ hop, enabled }) => ({ hop, enabled }));
|
|
11410
|
+
}
|
|
11411
|
+
return { ok: true, value: out };
|
|
11412
|
+
}
|
|
11413
|
+
function parseProviderChainOverrides(raw) {
|
|
11414
|
+
const decoded = decodeProviderChains(raw);
|
|
11415
|
+
return decoded.ok ? checkedProviderChains(decoded.value) : decoded;
|
|
11416
|
+
}
|
|
11417
|
+
function parseStoredProviderChainOverrides(raw) {
|
|
11418
|
+
const decoded = decodeProviderChains(raw);
|
|
11419
|
+
if (!decoded.ok)
|
|
11420
|
+
return decoded;
|
|
11421
|
+
const dropped = [];
|
|
11422
|
+
const kept = {};
|
|
11423
|
+
for (const [primary, entries] of Object.entries(decoded.value)) {
|
|
11424
|
+
if (!isChainablePrimary(primary) || !Array.isArray(entries)) {
|
|
11425
|
+
kept[primary] = entries;
|
|
11426
|
+
continue;
|
|
11427
|
+
}
|
|
11428
|
+
const allowed = allowedProviderChainHosts(primary);
|
|
11429
|
+
kept[primary] = entries.filter((entry) => {
|
|
11430
|
+
const hop = isPlainObject2(entry) ? entry.hop : void 0;
|
|
11431
|
+
if (typeof hop !== "string" || hop === "" || allowed.includes(hop))
|
|
11432
|
+
return true;
|
|
11433
|
+
dropped.push({ primary, hop: hop.slice(0, 80) });
|
|
11434
|
+
return false;
|
|
11435
|
+
});
|
|
11436
|
+
}
|
|
11437
|
+
const checked = checkedProviderChains(kept);
|
|
11438
|
+
return checked.ok ? { ...checked, dropped } : checked;
|
|
11439
|
+
}
|
|
11440
|
+
function validateProviderChainsSetting(raw) {
|
|
11441
|
+
const parsed = parseProviderChainOverrides(raw);
|
|
11442
|
+
return parsed.ok ? null : parsed.error;
|
|
10634
11443
|
}
|
|
10635
|
-
|
|
11444
|
+
function setProviderChainOverrides(value, onDropped) {
|
|
11445
|
+
const parsed = value === null || value === void 0 || value === "" ? null : parseStoredProviderChainOverrides(value);
|
|
11446
|
+
if (parsed?.ok && parsed.dropped.length > 0)
|
|
11447
|
+
onDropped?.(parsed.dropped);
|
|
11448
|
+
_overrides = parsed?.ok ? parsed.value : null;
|
|
11449
|
+
_overrideLeads = {};
|
|
11450
|
+
_overrideHops = {};
|
|
11451
|
+
for (const [primary, entries] of Object.entries(_overrides ?? {})) {
|
|
11452
|
+
const at = entries.findIndex((entry) => entry.hop === primary);
|
|
11453
|
+
const on = (list) => list.filter((entry) => entry.enabled).map((entry) => entry.hop);
|
|
11454
|
+
_overrideLeads[primary] = on(entries.slice(0, at));
|
|
11455
|
+
_overrideHops[primary] = on(entries.slice(at + 1));
|
|
11456
|
+
}
|
|
11457
|
+
return parsed === null || parsed.ok;
|
|
11458
|
+
}
|
|
11459
|
+
function getProviderChainOverrides() {
|
|
11460
|
+
return _overrides;
|
|
11461
|
+
}
|
|
11462
|
+
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;
|
|
10636
11463
|
var init_provider_chains = __esm({
|
|
10637
11464
|
"../shared/dist/provider-chains.js"() {
|
|
10638
11465
|
"use strict";
|
|
10639
11466
|
init_api_model_catalog();
|
|
11467
|
+
init_engine_spec();
|
|
10640
11468
|
PROVIDER_CHAINS = {
|
|
10641
|
-
"minimax-m3": ["
|
|
10642
|
-
"minimax-m3.1-flash": ["
|
|
11469
|
+
"minimax-m3": ["opencode-go:minimax-m3", "openrouter:minimax/minimax-m3"],
|
|
11470
|
+
"minimax-m3.1-flash": ["opencode-go:minimax-m3.1-flash", "openrouter:minimax/minimax-m3.1-flash"],
|
|
10643
11471
|
"muse-spark-1.3-contributor": ["openrouter:meta/muse-spark-1.3-contributor"],
|
|
10644
|
-
"
|
|
10645
|
-
"
|
|
10646
|
-
"
|
|
11472
|
+
"claude-haiku-5-5": ["opencode-go:claude-haiku-5-5", "openrouter:anthropic/claude-haiku-5.5"],
|
|
11473
|
+
"gemini-3.1-pro-preview": ["openrouter:google/gemini-3.1-pro-preview"],
|
|
11474
|
+
"deepseek-flash": ["opencode-go:deepseek-v4.1-flash", "openrouter:deepseek/deepseek-v4.1-flash"],
|
|
11475
|
+
"deepseek-v4-pro": ["opencode-go:deepseek-v4-pro", "openrouter:deepseek/deepseek-v4-pro"]
|
|
10647
11476
|
};
|
|
11477
|
+
PROVIDER_CHAIN_LEADS = {
|
|
11478
|
+
"openrouter:z-ai/glm-5.3-flash": ["zai:glm-5.3-flash", "opencode-go:glm-5.3-flash"],
|
|
11479
|
+
"openrouter:z-ai/glm-5.3": ["zai:glm-5.3", "opencode-go:glm-5.3"],
|
|
11480
|
+
"openrouter:z-ai/glm-5.2": ["zai:glm-5.2", "opencode-go:glm-5.2"]
|
|
11481
|
+
};
|
|
11482
|
+
PROVIDER_HOST_NOT_OFFERED = {
|
|
11483
|
+
"muse-spark-1.3-contributor": { "opencode-go": "OpenCode Go serves Muse only on its Responses API" },
|
|
11484
|
+
"gemini-3.1-pro-preview": { "opencode-go": "OpenCode Go does not serve Gemini" }
|
|
11485
|
+
};
|
|
11486
|
+
PROVIDER_CHAIN_MAX_ENTRIES = 8;
|
|
11487
|
+
PROVIDER_CHAINS_SETTING_MAX_CHARS = 16e3;
|
|
11488
|
+
PROVIDER_CHAIN_MAX_PRIMARIES = 32;
|
|
11489
|
+
ALLOWED_PROVIDER_CHAIN_HOPS = /* @__PURE__ */ new Set([
|
|
11490
|
+
...Object.values(PROVIDER_CHAINS).flat(),
|
|
11491
|
+
...Object.values(PROVIDER_CHAIN_LEADS).flat()
|
|
11492
|
+
]);
|
|
11493
|
+
_overrides = null;
|
|
11494
|
+
_overrideLeads = {};
|
|
11495
|
+
_overrideHops = {};
|
|
11496
|
+
}
|
|
11497
|
+
});
|
|
11498
|
+
|
|
11499
|
+
// ../shared/dist/provider-accounts.js
|
|
11500
|
+
function modelNames(modelId) {
|
|
11501
|
+
const value = modelId.trim();
|
|
11502
|
+
const names = /* @__PURE__ */ new Set([value]);
|
|
11503
|
+
for (const prefix of HOST_PREFIXES) {
|
|
11504
|
+
if (!value.startsWith(prefix))
|
|
11505
|
+
continue;
|
|
11506
|
+
const bare = value.slice(prefix.length);
|
|
11507
|
+
names.add(bare);
|
|
11508
|
+
const slash = bare.lastIndexOf("/");
|
|
11509
|
+
if (slash >= 0)
|
|
11510
|
+
names.add(bare.slice(slash + 1));
|
|
11511
|
+
}
|
|
11512
|
+
const canonical = canonicalModelId(value);
|
|
11513
|
+
if (canonical) {
|
|
11514
|
+
names.add(canonical);
|
|
11515
|
+
const entry = API_MODEL_CATALOG.find((m) => m.id === canonical || m.apiModel === canonical);
|
|
11516
|
+
if (entry) {
|
|
11517
|
+
names.add(entry.id);
|
|
11518
|
+
if (entry.apiModel)
|
|
11519
|
+
names.add(entry.apiModel);
|
|
11520
|
+
}
|
|
11521
|
+
}
|
|
11522
|
+
return names;
|
|
11523
|
+
}
|
|
11524
|
+
function accountServesModel(models, modelId) {
|
|
11525
|
+
if (!models || models.length === 0)
|
|
11526
|
+
return true;
|
|
11527
|
+
if (!modelId)
|
|
11528
|
+
return false;
|
|
11529
|
+
const names = modelNames(modelId);
|
|
11530
|
+
return models.some((model) => names.has(model.trim()));
|
|
11531
|
+
}
|
|
11532
|
+
function normalizeProviderAccountModels(models) {
|
|
11533
|
+
if (!Array.isArray(models))
|
|
11534
|
+
return null;
|
|
11535
|
+
const out = [];
|
|
11536
|
+
for (const model of models) {
|
|
11537
|
+
if (typeof model !== "string")
|
|
11538
|
+
return null;
|
|
11539
|
+
const trimmed = model.trim();
|
|
11540
|
+
if (!PROVIDER_ACCOUNT_MODEL_PATTERN.test(trimmed) || trimmed.includes(".."))
|
|
11541
|
+
return null;
|
|
11542
|
+
if (!out.includes(trimmed))
|
|
11543
|
+
out.push(trimmed);
|
|
11544
|
+
}
|
|
11545
|
+
return out.length > MAX_PROVIDER_ACCOUNT_MODELS ? null : out;
|
|
11546
|
+
}
|
|
11547
|
+
function parseProviderAccountLists(value, providers) {
|
|
11548
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
11549
|
+
return null;
|
|
11550
|
+
const parsed = {};
|
|
11551
|
+
for (const provider of providers) {
|
|
11552
|
+
const list = value[provider];
|
|
11553
|
+
if (!Array.isArray(list))
|
|
11554
|
+
continue;
|
|
11555
|
+
const accounts = [];
|
|
11556
|
+
for (const raw of list) {
|
|
11557
|
+
if (!raw || typeof raw !== "object")
|
|
11558
|
+
continue;
|
|
11559
|
+
const { id, label, key, models } = raw;
|
|
11560
|
+
if (typeof id !== "string" || !id.trim() || typeof key !== "string" || !key.trim())
|
|
11561
|
+
continue;
|
|
11562
|
+
const modelList = Array.isArray(models) ? models.filter((m) => typeof m === "string" && m.trim() !== "").map((m) => m.trim()) : [];
|
|
11563
|
+
accounts.push({ id: id.trim(), label: typeof label === "string" && label.trim() ? label.trim() : null, key: key.trim(), models: modelList });
|
|
11564
|
+
}
|
|
11565
|
+
if (accounts.length > 0)
|
|
11566
|
+
parsed[provider] = accounts;
|
|
11567
|
+
}
|
|
11568
|
+
return parsed;
|
|
11569
|
+
}
|
|
11570
|
+
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;
|
|
11571
|
+
var init_provider_accounts = __esm({
|
|
11572
|
+
"../shared/dist/provider-accounts.js"() {
|
|
11573
|
+
"use strict";
|
|
11574
|
+
init_api_model_catalog();
|
|
11575
|
+
DEFAULT_PROVIDER_ACCOUNT_ID = "primary";
|
|
11576
|
+
PROVIDER_ACCOUNT_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,39}$/;
|
|
11577
|
+
MAX_PROVIDER_ACCOUNTS = 10;
|
|
11578
|
+
MAX_PROVIDER_ACCOUNT_MODELS = 50;
|
|
11579
|
+
PROVIDER_ACCOUNT_MODEL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,120}$/;
|
|
11580
|
+
OPENCODE_GO_DEEPSEEK_GLM_MODELS = [
|
|
11581
|
+
"deepseek-v4.1-flash",
|
|
11582
|
+
"deepseek-v4-pro",
|
|
11583
|
+
"glm-5.3-flash",
|
|
11584
|
+
"glm-5.3",
|
|
11585
|
+
"glm-5.2"
|
|
11586
|
+
];
|
|
11587
|
+
HOST_PREFIXES = ["opencode-go:", "zai:", "openrouter:"];
|
|
10648
11588
|
}
|
|
10649
11589
|
});
|
|
10650
11590
|
|
|
@@ -10763,6 +11703,25 @@ var init_project_engine_overrides = __esm({
|
|
|
10763
11703
|
}
|
|
10764
11704
|
});
|
|
10765
11705
|
|
|
11706
|
+
// ../shared/dist/trailing-slashes.js
|
|
11707
|
+
function stripTrailingSlashes(value) {
|
|
11708
|
+
let end = value.length;
|
|
11709
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47)
|
|
11710
|
+
end--;
|
|
11711
|
+
return end === value.length ? value : value.slice(0, end);
|
|
11712
|
+
}
|
|
11713
|
+
function stripTrailingChars(value, isTrailing) {
|
|
11714
|
+
let end = value.length;
|
|
11715
|
+
while (end > 0 && isTrailing.test(value[end - 1]))
|
|
11716
|
+
end--;
|
|
11717
|
+
return end === value.length ? value : value.slice(0, end);
|
|
11718
|
+
}
|
|
11719
|
+
var init_trailing_slashes = __esm({
|
|
11720
|
+
"../shared/dist/trailing-slashes.js"() {
|
|
11721
|
+
"use strict";
|
|
11722
|
+
}
|
|
11723
|
+
});
|
|
11724
|
+
|
|
10766
11725
|
// ../shared/dist/gemini-visual.js
|
|
10767
11726
|
function visualProDailyBudget(raw) {
|
|
10768
11727
|
const parsed = raw && /^\s*\d{1,6}\s*$/.test(raw) ? Number(raw.trim()) : NaN;
|
|
@@ -10776,7 +11735,7 @@ function isRetiredGeminiModel(id) {
|
|
|
10776
11735
|
}
|
|
10777
11736
|
function geminiNativeModelsUrl(base = process.env.GEMINI_API_BASE) {
|
|
10778
11737
|
const raw = base && base.trim() || GEMINI_OPENAI_BASE;
|
|
10779
|
-
const native = raw.replace(/\/openai\/?$/, "/")
|
|
11738
|
+
const native = stripTrailingSlashes(raw.replace(/\/openai\/?$/, "/"));
|
|
10780
11739
|
return `${native}/models?pageSize=1000`;
|
|
10781
11740
|
}
|
|
10782
11741
|
function asRecord(value) {
|
|
@@ -10896,6 +11855,7 @@ var KNOWN_INPUT_USD_PER_M, GEMINI_OPENAI_BASE, DEFAULT_GEMINI_SCREEN_MODEL, DEFA
|
|
|
10896
11855
|
var init_gemini_visual = __esm({
|
|
10897
11856
|
"../shared/dist/gemini-visual.js"() {
|
|
10898
11857
|
"use strict";
|
|
11858
|
+
init_trailing_slashes();
|
|
10899
11859
|
KNOWN_INPUT_USD_PER_M = {
|
|
10900
11860
|
"gemini-2.5-flash-lite": 0.1,
|
|
10901
11861
|
"gemini-3.1-flash-lite": 0.25,
|
|
@@ -12238,12 +13198,13 @@ function truncateAtWord(text, max) {
|
|
|
12238
13198
|
const slice = trimmed.slice(0, room + 1);
|
|
12239
13199
|
const lastSpace = slice.lastIndexOf(" ");
|
|
12240
13200
|
const cut = lastSpace > room * 0.5 ? slice.slice(0, lastSpace) : trimmed.slice(0, room);
|
|
12241
|
-
return `${cut
|
|
13201
|
+
return `${stripTrailingChars(cut, TRAILING_PUNCTUATION)}\u2026`;
|
|
12242
13202
|
}
|
|
12243
|
-
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;
|
|
13203
|
+
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;
|
|
12244
13204
|
var init_canvas_support = __esm({
|
|
12245
13205
|
"../shared/dist/canvas-support.js"() {
|
|
12246
13206
|
"use strict";
|
|
13207
|
+
init_trailing_slashes();
|
|
12247
13208
|
CANVAS_UNSUPPORTED_CODE = "canvas_unsupported";
|
|
12248
13209
|
CANVAS_UNSUPPORTED_TITLE = "Games and canvas apps aren't supported yet";
|
|
12249
13210
|
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.";
|
|
@@ -12368,6 +13329,7 @@ var init_canvas_support = __esm({
|
|
|
12368
13329
|
TOUCH_NONE_RE = /touch-action\s{0,8}:\s{0,8}none/;
|
|
12369
13330
|
CODE_TAGS = /* @__PURE__ */ new Set(["script", "link", "flt-glass-pane", "flutter-view", "flt-scene-host"]);
|
|
12370
13331
|
STATIC_STANDARD_MIN_INTERACTIVE = 3;
|
|
13332
|
+
TRAILING_PUNCTUATION = /[\s,;:.\-–—]/u;
|
|
12371
13333
|
}
|
|
12372
13334
|
});
|
|
12373
13335
|
|
|
@@ -12550,22 +13512,22 @@ function joinChainedLines(code) {
|
|
|
12550
13512
|
const trimmed = lines[i].trimStart();
|
|
12551
13513
|
if (last !== -1 && trimmed.startsWith(".") && !trimmed.startsWith("...")) {
|
|
12552
13514
|
if (!lastStripped) {
|
|
12553
|
-
lines[last] =
|
|
13515
|
+
lines[last] = stripLineComment3(lines[last]).trimEnd();
|
|
12554
13516
|
lastStripped = true;
|
|
12555
13517
|
}
|
|
12556
13518
|
if (lines[last].length <= MAX_LINE_CHARS)
|
|
12557
|
-
lines[last] = `${lines[last]} ${
|
|
13519
|
+
lines[last] = `${lines[last]} ${stripLineComment3(trimmed).trimEnd()}`;
|
|
12558
13520
|
lines[i] = "";
|
|
12559
13521
|
continue;
|
|
12560
13522
|
}
|
|
12561
|
-
if (
|
|
13523
|
+
if (stripLineComment3(lines[i]).trim()) {
|
|
12562
13524
|
last = i;
|
|
12563
13525
|
lastStripped = false;
|
|
12564
13526
|
}
|
|
12565
13527
|
}
|
|
12566
13528
|
return lines.join("\n");
|
|
12567
13529
|
}
|
|
12568
|
-
function
|
|
13530
|
+
function stripLineComment3(rawLine) {
|
|
12569
13531
|
if (rawLine.trimStart().startsWith("//"))
|
|
12570
13532
|
return "";
|
|
12571
13533
|
const at = rawLine.indexOf(" // ");
|
|
@@ -12581,7 +13543,7 @@ function stateLiteralsOf(line) {
|
|
|
12581
13543
|
const out = [];
|
|
12582
13544
|
for (const m of line.matchAll(STRING_LITERAL_RE)) {
|
|
12583
13545
|
const text = m[1] ?? m[2] ?? m[3] ?? "";
|
|
12584
|
-
if (
|
|
13546
|
+
if (PLACEHOLDER_ATTR_RE.test(text))
|
|
12585
13547
|
continue;
|
|
12586
13548
|
if (PLACEHOLDER_CALL_TAIL_RE.test(line.slice(Math.max(0, m.index - 40), m.index)))
|
|
12587
13549
|
continue;
|
|
@@ -12614,7 +13576,7 @@ function analyzeReadinessPolling(rawCode) {
|
|
|
12614
13576
|
offset += rawLine.length + 1;
|
|
12615
13577
|
if (rawLine.length > MAX_LINE_CHARS)
|
|
12616
13578
|
continue;
|
|
12617
|
-
const line =
|
|
13579
|
+
const line = stripLineComment3(rawLine);
|
|
12618
13580
|
if (!line.trim())
|
|
12619
13581
|
continue;
|
|
12620
13582
|
const literals = literalsOf(line);
|
|
@@ -12677,7 +13639,7 @@ function formatReadinessPollAdvisory(findings) {
|
|
|
12677
13639
|
const where = findings.length === 1 ? "One place" : `${findings.length} places`;
|
|
12678
13640
|
return `${where} re-check a background state once and assert the finished state at once (${short(first.line, 90)} then ${short(first.assertion, 90)}). ${first.reason}`;
|
|
12679
13641
|
}
|
|
12680
|
-
var READINESS_POLL_MAX_CODE_CHARS, MAX_LINE_CHARS, MAX_FINDINGS, ASSERTION_WINDOW_LINES, MAX_BLOCK_CHARS, MAX_HEAD_CHARS, STRING_LITERAL_RE, RECHECK_NAME_RE, RETRY_NAME_RE, PENDING_STATE_RE, LOCATOR_LINE_RE, CLICK_RE, RELOAD_RE, GOTO_RE, ACTION_RE, EXPECT_RE, NEGATED_RE, PENDING_ENDED_RE, POSITIVE_MATCHER_RE, ZERO_COUNT_RE, TEST_END_RE, TEST_START_RE, POLL_OPEN_RE, PLACEHOLDER_CALL_TAIL_RE, POLL_SHAPE;
|
|
13642
|
+
var READINESS_POLL_MAX_CODE_CHARS, MAX_LINE_CHARS, MAX_FINDINGS, ASSERTION_WINDOW_LINES, MAX_BLOCK_CHARS, MAX_HEAD_CHARS, STRING_LITERAL_RE, RECHECK_NAME_RE, RETRY_NAME_RE, PENDING_STATE_RE, LOCATOR_LINE_RE, CLICK_RE, RELOAD_RE, GOTO_RE, ACTION_RE, EXPECT_RE, NEGATED_RE, PENDING_ENDED_RE, POSITIVE_MATCHER_RE, ZERO_COUNT_RE, TEST_END_RE, TEST_START_RE, POLL_OPEN_RE, PLACEHOLDER_ATTR_RE, PLACEHOLDER_CALL_TAIL_RE, POLL_SHAPE;
|
|
12681
13643
|
var init_readiness_poll = __esm({
|
|
12682
13644
|
"../shared/dist/readiness-poll.js"() {
|
|
12683
13645
|
"use strict";
|
|
@@ -12690,7 +13652,7 @@ var init_readiness_poll = __esm({
|
|
|
12690
13652
|
STRING_LITERAL_RE = /'((?:\\.|[^'\\\n]){0,300})'|"((?:\\.|[^"\\\n]){0,300})"|`((?:\\.|[^`\\\n]){0,300})`/g;
|
|
12691
13653
|
RECHECK_NAME_RE = /^\s{0,10}(?:check again|check (?:status|availability|now|for updates|progress)|re-?check(?: status)?|update status|see (?:latest|updates))\s{0,10}(?:\.\.\.|…)?\s{0,10}$/i;
|
|
12692
13654
|
RETRY_NAME_RE = /^\s{0,10}(?:refresh(?: (?:status|list|page|results|now))?|reload(?: (?:page|results|list))?|try again|retry|load (?:again|latest))\s{0,10}(?:\.\.\.|…)?\s{0,10}$/i;
|
|
12693
|
-
PENDING_STATE_RE = /\b(?:searching|checking|sending|pending|processing|queued|in progress|loading|generating|calculating|preparing|verifying|syncing|refreshing|not ready|please wait|waiting)\b|\b[a-z]{3,
|
|
13655
|
+
PENDING_STATE_RE = /\b(?:searching|checking|sending|pending|processing|queued|in progress|loading|generating|calculating|preparing|verifying|syncing|refreshing|not ready|please wait|waiting)\b|\b(?:saving|uploading|downloading|signing|logging|submitting|creating|updating|deleting|importing|exporting|publishing|connecting|redirecting|authenticating|installing|retrieving|fetching|analy[sz]ing|running|rendering|restarting|building|compiling|booting|provisioning|deploying|indexing|migrating|launching|initiali[sz]ing|optimi[sz]ing|converting|processing)(?:\s{1,3}[a-z]{1,20}){0,2}\s{0,3}(?:\.\.\.|…)\s{0,10}$|^\s{0,10}(?:starting|scanning|working|applying|finishing)(?:\s{1,3}[a-z]{1,20}){0,2}\s{0,3}(?:\.\.\.|…)\s{0,10}$|^\s{0,10}[a-z]{3,40}(?<!th)ing\s{0,3}(?:\.\.\.|…)\s{0,10}$/i;
|
|
12694
13656
|
LOCATOR_LINE_RE = /\.(?:getBy[A-Z]\w{0,30}|locator|toHaveText|toContainText)\s{0,20}\(/;
|
|
12695
13657
|
CLICK_RE = /\.(?:click|dblclick|tap)\s{0,20}\(/;
|
|
12696
13658
|
RELOAD_RE = /\bpage\.reload\s{0,20}\(/;
|
|
@@ -12704,6 +13666,7 @@ var init_readiness_poll = __esm({
|
|
|
12704
13666
|
TEST_END_RE = /^\s{0,20}\}\s{0,20}\)\s{0,20};?\s{0,20}$/;
|
|
12705
13667
|
TEST_START_RE = /\btest(?:\.\w{1,20})?\s{0,20}\(/;
|
|
12706
13668
|
POLL_OPEN_RE = /\bexpect(?:\.soft)?\s{0,20}\(\s{0,20}async\b|\bexpect\.poll\s{0,20}\(|\bfor\s{0,20}\(|\bwhile\s{0,20}\(|\bdo\s{0,20}\{/g;
|
|
13669
|
+
PLACEHOLDER_ATTR_RE = /placeholder\s{0,4}[*^$~|]?=/;
|
|
12707
13670
|
PLACEHOLDER_CALL_TAIL_RE = /getByPlaceholder\s{0,20}\(\s{0,20}$/;
|
|
12708
13671
|
POLL_SHAPE = "await expect(async () => { await page.reload(); await expect(<the finished state>).toBeVisible({ timeout: 1500 }); }).toPass({ timeout: 20000 });";
|
|
12709
13672
|
}
|
|
@@ -12725,7 +13688,7 @@ function blankBlockComments2(code) {
|
|
|
12725
13688
|
from = close + 2;
|
|
12726
13689
|
}
|
|
12727
13690
|
}
|
|
12728
|
-
function
|
|
13691
|
+
function stripLineComment4(rawLine) {
|
|
12729
13692
|
if (rawLine.trimStart().startsWith("//"))
|
|
12730
13693
|
return "";
|
|
12731
13694
|
const at = rawLine.indexOf(" // ");
|
|
@@ -12736,7 +13699,7 @@ function logicalLines(code) {
|
|
|
12736
13699
|
for (const raw of code.split("\n")) {
|
|
12737
13700
|
if (raw.length > MAX_LINE_CHARS2)
|
|
12738
13701
|
continue;
|
|
12739
|
-
const line =
|
|
13702
|
+
const line = stripLineComment4(raw).trimEnd();
|
|
12740
13703
|
if (!line.trim())
|
|
12741
13704
|
continue;
|
|
12742
13705
|
const trimmed = line.trimStart();
|
|
@@ -13098,13 +14061,33 @@ function short4(text, max) {
|
|
|
13098
14061
|
const flat = text.replace(/\s+/g, " ").trim();
|
|
13099
14062
|
return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
|
|
13100
14063
|
}
|
|
14064
|
+
function stripFractionZeros(s) {
|
|
14065
|
+
let end = s.length;
|
|
14066
|
+
while (end > 0 && s[end - 1] === "0")
|
|
14067
|
+
end--;
|
|
14068
|
+
if (end === s.length)
|
|
14069
|
+
return s;
|
|
14070
|
+
const dot = s.lastIndexOf(".");
|
|
14071
|
+
if (dot < 0)
|
|
14072
|
+
return s;
|
|
14073
|
+
return s.slice(0, end === dot + 1 ? dot : end);
|
|
14074
|
+
}
|
|
13101
14075
|
function digitsOf(token) {
|
|
13102
|
-
return token.replace(/[^\d.]/g, "")
|
|
14076
|
+
return stripFractionZeros(token.replace(/[^\d.]/g, "")).replace(/^0+(?=\d)/, "");
|
|
13103
14077
|
}
|
|
13104
14078
|
function computes(line) {
|
|
13105
14079
|
const code = line.replace(STRING_RE, '""').replace(REGEX_LITERAL_RE, "$1RX").replace(LITERAL_ARITH_RE, "N");
|
|
13106
14080
|
return COMPUTE_RE.test(code) || RELATIONAL_RE.test(line);
|
|
13107
14081
|
}
|
|
14082
|
+
function identifiersIn(line) {
|
|
14083
|
+
const code = line.replace(STRING_RE, '""').replace(REGEX_LITERAL_RE, "$1RX");
|
|
14084
|
+
return [code, ...[...line.matchAll(TEMPLATE_EXPR_RE)].map((m) => m[1])].flatMap((part) => part.match(IDENTIFIER_RE) ?? []);
|
|
14085
|
+
}
|
|
14086
|
+
function addTyped(typed, value) {
|
|
14087
|
+
const d = digitsOf(value);
|
|
14088
|
+
if (/\d/.test(d))
|
|
14089
|
+
typed.add(d);
|
|
14090
|
+
}
|
|
13108
14091
|
function reasonFor4(kind, label, value) {
|
|
13109
14092
|
const what = kind === "derived-value-shape-only" ? `The ${label} is checked only for its shape, so any amount passes, including a wrongly computed one.` : `The ${label} is checked against a number written into the test (${value}). That number was read off the page, so when the page computes it wrong the check still passes.`;
|
|
13110
14093
|
return `${what} Instead, ${COMPUTE_SHAPE} If the number is not computed from other values (a list price, an id, a fixed fee), leave it as it is. Keep the existing checks.`;
|
|
@@ -13116,25 +14099,32 @@ function analyzeDerivedValues(rawCode) {
|
|
|
13116
14099
|
for (const body of testBodies(rawCode)) {
|
|
13117
14100
|
if (findings.length >= MAX_FINDINGS4)
|
|
13118
14101
|
break;
|
|
13119
|
-
if (body.some(computes))
|
|
13120
|
-
continue;
|
|
13121
14102
|
const typed = /* @__PURE__ */ new Set();
|
|
13122
14103
|
const definitions = /* @__PURE__ */ new Map();
|
|
14104
|
+
const computed = /* @__PURE__ */ new Set();
|
|
14105
|
+
const usesComputed = (text) => identifiersIn(text).some((id) => computed.has(id));
|
|
13123
14106
|
for (const line of body) {
|
|
13124
14107
|
if (FILL_RE2.test(line) && !EXPECT_RE3.test(line)) {
|
|
13125
|
-
for (const m of line.matchAll(
|
|
13126
|
-
|
|
13127
|
-
|
|
13128
|
-
|
|
13129
|
-
|
|
14108
|
+
for (const m of line.matchAll(QUOTED_RE))
|
|
14109
|
+
addTyped(typed, m[1]);
|
|
14110
|
+
const arg = FILL_ARG_RE.exec(line)?.[1];
|
|
14111
|
+
const literal = arg === void 0 ? null : LITERAL_VALUE_RE.exec(definitions.get(arg) ?? "");
|
|
14112
|
+
const value = literal?.slice(1).find((v) => v !== void 0);
|
|
14113
|
+
if (value !== void 0)
|
|
14114
|
+
addTyped(typed, value);
|
|
13130
14115
|
}
|
|
13131
14116
|
const def = DEFINITION_RE.exec(line);
|
|
13132
|
-
if (def)
|
|
14117
|
+
if (def) {
|
|
13133
14118
|
definitions.set(def[1], def[2]);
|
|
14119
|
+
if (computes(def[2]) || usesComputed(def[2]))
|
|
14120
|
+
computed.add(def[1]);
|
|
14121
|
+
}
|
|
13134
14122
|
}
|
|
13135
14123
|
for (const line of body) {
|
|
13136
14124
|
if (!EXPECT_RE3.test(line) || !VALUE_MATCHER_RE2.test(line) || /\.not\s{0,20}\./.test(line))
|
|
13137
14125
|
continue;
|
|
14126
|
+
if (computes(line) || usesComputed(line))
|
|
14127
|
+
continue;
|
|
13138
14128
|
const subject = EXPECT_SUBJECT_RE.exec(line)?.[1];
|
|
13139
14129
|
const label = DERIVED_WORD_RE.exec(`${line} ${(subject && definitions.get(subject)) ?? ""}`)?.[0];
|
|
13140
14130
|
if (!label)
|
|
@@ -13164,7 +14154,7 @@ function formatDerivedValueAdvisory(findings) {
|
|
|
13164
14154
|
const where = findings.length === 1 ? "One computed value is" : `${findings.length} computed values are`;
|
|
13165
14155
|
return `${where} checked against a copied number or only its shape (${short4(first.line, 90)}). ${first.reason}`;
|
|
13166
14156
|
}
|
|
13167
|
-
var MAX_FINDINGS4, EXPECT_RE3, DERIVED_WORD_RE, VALUE_MATCHER_RE2, MONEY_TOKEN_RE, SHAPE_ARG_RE, FILL_RE2, EXPECT_MESSAGE_RE, STRING_RE, REGEX_LITERAL_RE, COMPUTE_RE, RELATIONAL_RE, DEFINITION_RE, EXPECT_SUBJECT_RE, LITERAL_ARITH_RE, COMPUTE_SHAPE;
|
|
14157
|
+
var MAX_FINDINGS4, EXPECT_RE3, DERIVED_WORD_RE, VALUE_MATCHER_RE2, MONEY_TOKEN_RE, SHAPE_ARG_RE, FILL_RE2, EXPECT_MESSAGE_RE, STRING_RE, REGEX_LITERAL_RE, COMPUTE_RE, RELATIONAL_RE, DEFINITION_RE, EXPECT_SUBJECT_RE, LITERAL_ARITH_RE, TEMPLATE_EXPR_RE, IDENTIFIER_RE, QUOTED_RE, FILL_ARG_RE, LITERAL_VALUE_RE, COMPUTE_SHAPE;
|
|
13168
14158
|
var init_derived_value_check = __esm({
|
|
13169
14159
|
"../shared/dist/derived-value-check.js"() {
|
|
13170
14160
|
"use strict";
|
|
@@ -13184,6 +14174,11 @@ var init_derived_value_check = __esm({
|
|
|
13184
14174
|
DEFINITION_RE = /\b(?:const|let)\s{1,10}([\w$]{1,60})\s{0,10}=\s{0,10}([^\n]{0,300})/;
|
|
13185
14175
|
EXPECT_SUBJECT_RE = /\bexpect(?:\.soft)?\s{0,20}\(\s{0,20}([\w$]{1,60})/;
|
|
13186
14176
|
LITERAL_ARITH_RE = /\b\d[\d_.]{0,15}(?:\s{0,5}[*/+-]\s{0,5}\d[\d_.]{0,15}){1,8}\b/g;
|
|
14177
|
+
TEMPLATE_EXPR_RE = /\$\{([^{}\n]{0,200})\}/g;
|
|
14178
|
+
IDENTIFIER_RE = /[A-Za-z_$][\w$]{0,60}/g;
|
|
14179
|
+
QUOTED_RE = /['"`]([^'"`\n]{0,40})['"`]/g;
|
|
14180
|
+
FILL_ARG_RE = /\.(?:fill|type|pressSequentially|selectOption)\s{0,20}\(\s{0,20}([A-Za-z_$][\w$]{0,60})\s{0,20}[,)]/;
|
|
14181
|
+
LITERAL_VALUE_RE = /^\s{0,10}(?:'([^'\n]{0,40})'|"([^"\n]{0,40})"|`([^`$\n]{0,40})`|(\d[\d_,]{0,15}(?:\.\d{1,4})?))\s{0,10};?\s{0,10}$/;
|
|
13187
14182
|
COMPUTE_SHAPE = "compute the expected value in the test from its inputs (the values the test typed, or each input read from the page) and compare: const toNumber = (s) => Number(s.replace(/[^\\d.-]/g, '')); const unit = toNumber(await <unit price locator>.innerText()); then expect(toNumber(await <shown value locator>.innerText())).toBeCloseTo(unit * quantity, 2); for several lines, read them all and sum them with reduce. For a comma-decimal price like \u20AC1.234,56, drop the dots and turn the comma into a point before Number, since the replace above would read it as 1.23456.";
|
|
13188
14183
|
}
|
|
13189
14184
|
});
|
|
@@ -13296,7 +14291,7 @@ function findExpectCalls(code) {
|
|
|
13296
14291
|
if (!subj)
|
|
13297
14292
|
continue;
|
|
13298
14293
|
const rest = code.slice(subj.end + 1, subj.end + 1 + 80);
|
|
13299
|
-
const mm = /^\s*((?:\.\s*not)
|
|
14294
|
+
const mm = /^\s*((?:\.\s*not\s*)?\.\s*(\w+))\s{0,20}\(/.exec(rest);
|
|
13300
14295
|
if (!mm)
|
|
13301
14296
|
continue;
|
|
13302
14297
|
const argStart = subj.end + 1 + mm[0].length;
|
|
@@ -15218,6 +16213,133 @@ function formatStaleAfterSubmitAdvisory(rawCode) {
|
|
|
15218
16213
|
const more = described.length > 3 ? ` (+${described.length - 3} more)` : "";
|
|
15219
16214
|
return `stale-after-submit: ${described.slice(0, 3).join("; ")}${more}; these pass before the request lands and still pass if the server rejects or ignores the save. Wait for the result instead: page.waitForResponse around the click, or assert something the submit creates (the new row, the confirmation message, a path with an end anchor such as /\\/orders\\/\\d+$/)`;
|
|
15220
16215
|
}
|
|
16216
|
+
function regexLiteralEnd4(text, at) {
|
|
16217
|
+
let inClass = false;
|
|
16218
|
+
for (let i = at + 1; i < text.length; i++) {
|
|
16219
|
+
const ch = text[i];
|
|
16220
|
+
if (ch === "\n")
|
|
16221
|
+
return -1;
|
|
16222
|
+
if (ch === "\\") {
|
|
16223
|
+
i++;
|
|
16224
|
+
continue;
|
|
16225
|
+
}
|
|
16226
|
+
if (inClass) {
|
|
16227
|
+
if (ch === "]")
|
|
16228
|
+
inClass = false;
|
|
16229
|
+
continue;
|
|
16230
|
+
}
|
|
16231
|
+
if (ch === "[") {
|
|
16232
|
+
inClass = true;
|
|
16233
|
+
continue;
|
|
16234
|
+
}
|
|
16235
|
+
if (ch === "/")
|
|
16236
|
+
return i + 1;
|
|
16237
|
+
}
|
|
16238
|
+
return -1;
|
|
16239
|
+
}
|
|
16240
|
+
function startsWithRegexLiteral(text) {
|
|
16241
|
+
const t = text.trimStart();
|
|
16242
|
+
return t[0] === "/" && t[1] !== "/" && t[1] !== "*" && regexLiteralEnd4(t, 0) !== -1;
|
|
16243
|
+
}
|
|
16244
|
+
function isBareRegexLiteral(text) {
|
|
16245
|
+
const t = text.trimStart();
|
|
16246
|
+
if (t[0] !== "/" || t[1] === "/" || t[1] === "*")
|
|
16247
|
+
return false;
|
|
16248
|
+
const end = regexLiteralEnd4(t, 0);
|
|
16249
|
+
if (end === -1)
|
|
16250
|
+
return false;
|
|
16251
|
+
let i = end;
|
|
16252
|
+
while (i < t.length && /[a-z]/.test(t[i]))
|
|
16253
|
+
i++;
|
|
16254
|
+
while (i < t.length && /\s/.test(t[i]))
|
|
16255
|
+
i++;
|
|
16256
|
+
return i >= t.length || t[i] === ",";
|
|
16257
|
+
}
|
|
16258
|
+
function regexDeclaredNames(code) {
|
|
16259
|
+
const names = /* @__PURE__ */ new Set();
|
|
16260
|
+
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;
|
|
16261
|
+
let m;
|
|
16262
|
+
while ((m = decl.exec(code)) !== null)
|
|
16263
|
+
names.add(m[1]);
|
|
16264
|
+
return names;
|
|
16265
|
+
}
|
|
16266
|
+
function afterFirstArg(args) {
|
|
16267
|
+
let depth = 0;
|
|
16268
|
+
let quote = null;
|
|
16269
|
+
for (let i = 0; i < args.length; i++) {
|
|
16270
|
+
const ch = args[i];
|
|
16271
|
+
if (quote) {
|
|
16272
|
+
if (ch === "\\") {
|
|
16273
|
+
i++;
|
|
16274
|
+
continue;
|
|
16275
|
+
}
|
|
16276
|
+
if (ch === quote)
|
|
16277
|
+
quote = null;
|
|
16278
|
+
continue;
|
|
16279
|
+
}
|
|
16280
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
16281
|
+
quote = ch;
|
|
16282
|
+
continue;
|
|
16283
|
+
}
|
|
16284
|
+
if (ch === "(" || ch === "[" || ch === "{")
|
|
16285
|
+
depth++;
|
|
16286
|
+
else if (ch === ")" || ch === "]" || ch === "}")
|
|
16287
|
+
depth--;
|
|
16288
|
+
else if (ch === "," && depth === 0)
|
|
16289
|
+
return args.slice(i + 1);
|
|
16290
|
+
}
|
|
16291
|
+
return "";
|
|
16292
|
+
}
|
|
16293
|
+
function analyzeRegexMatchers(rawCode) {
|
|
16294
|
+
if (!rawCode || typeof rawCode !== "string")
|
|
16295
|
+
return [];
|
|
16296
|
+
const code = stripComments2(rawCode.slice(0, MAX_LINT_INPUT));
|
|
16297
|
+
const out = [];
|
|
16298
|
+
const re = /\bexpect(?:\.soft)?\s{0,20}\(/g;
|
|
16299
|
+
let m;
|
|
16300
|
+
let examined = 0;
|
|
16301
|
+
let line = 1;
|
|
16302
|
+
let lineAt2 = 0;
|
|
16303
|
+
let regexNames;
|
|
16304
|
+
while ((m = re.exec(code)) !== null && examined++ < MAX_CALLS && out.length < MAX_REGEX_MATCHER_FINDINGS) {
|
|
16305
|
+
const subj = readBalanced(code, m.index + m[0].length);
|
|
16306
|
+
if (!subj)
|
|
16307
|
+
continue;
|
|
16308
|
+
const mm = /^\s{0,20}(\.\s{0,20}not)?\s{0,20}\.\s{0,20}(\w{1,40})\s{0,20}\(/.exec(code.slice(subj.end + 1, subj.end + 1 + 80));
|
|
16309
|
+
if (!mm || !REGEX_EQUALITY_MATCHERS.has(mm[2]))
|
|
16310
|
+
continue;
|
|
16311
|
+
const args = readBalanced(code, subj.end + 1 + mm[0].length);
|
|
16312
|
+
if (!args)
|
|
16313
|
+
continue;
|
|
16314
|
+
const expected = mm[2] === "toHaveJSProperty" ? afterFirstArg(args.text) : args.text;
|
|
16315
|
+
if (!isBareRegexLiteral(expected))
|
|
16316
|
+
continue;
|
|
16317
|
+
if (mm[2] !== "toHaveJSProperty" && mm[2] !== "toContain") {
|
|
16318
|
+
const subject = subj.text.trim();
|
|
16319
|
+
if (startsWithRegexLiteral(subject) || /^new\s+RegExp\b/.test(subject))
|
|
16320
|
+
continue;
|
|
16321
|
+
if (/^[A-Za-z_$][\w$]{0,80}$/.test(subject)) {
|
|
16322
|
+
regexNames ??= regexDeclaredNames(code);
|
|
16323
|
+
if (regexNames.has(subject))
|
|
16324
|
+
continue;
|
|
16325
|
+
}
|
|
16326
|
+
}
|
|
16327
|
+
for (let i = code.indexOf("\n", lineAt2); i !== -1 && i < m.index; i = code.indexOf("\n", i + 1)) {
|
|
16328
|
+
line++;
|
|
16329
|
+
lineAt2 = i + 1;
|
|
16330
|
+
}
|
|
16331
|
+
out.push({ line, matcher: mm[2], negated: mm[1] !== void 0 });
|
|
16332
|
+
}
|
|
16333
|
+
return out;
|
|
16334
|
+
}
|
|
16335
|
+
function formatRegexMatcherNote(findings) {
|
|
16336
|
+
const lines = findings.map((f) => {
|
|
16337
|
+
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";
|
|
16338
|
+
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"}.`;
|
|
16339
|
+
});
|
|
16340
|
+
return `${lines.join("\n")}
|
|
16341
|
+
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.`;
|
|
16342
|
+
}
|
|
15221
16343
|
function lintTestQuality(rawCode, opts = {}) {
|
|
15222
16344
|
if (!rawCode || typeof rawCode !== "string")
|
|
15223
16345
|
return [];
|
|
@@ -15225,6 +16347,10 @@ function lintTestQuality(rawCode, opts = {}) {
|
|
|
15225
16347
|
return lintTestData(rawCode, { pageContacts: opts.pageContacts });
|
|
15226
16348
|
if (opts.only === "test-data-blocking")
|
|
15227
16349
|
return analyzeTestData(rawCode, { pageContacts: opts.pageContacts }).blocking;
|
|
16350
|
+
if (opts.only === "regex-matcher") {
|
|
16351
|
+
const found = analyzeRegexMatchers(rawCode);
|
|
16352
|
+
return found.length > 0 ? [formatRegexMatcherNote(found)] : [];
|
|
16353
|
+
}
|
|
15228
16354
|
const warnings = [];
|
|
15229
16355
|
if (rawCode.length > MAX_LINT_INPUT) {
|
|
15230
16356
|
warnings.push(`The code is ${Math.round(rawCode.length / 1024)} KB; the quality lint checked only the first ${MAX_LINT_INPUT / 1e3} KB. Split it into smaller tests (one behaviour per test).`);
|
|
@@ -15499,7 +16625,7 @@ function specItemsFromTestPlans(testPlans) {
|
|
|
15499
16625
|
}
|
|
15500
16626
|
return [...new Set(out)].slice(0, 200);
|
|
15501
16627
|
}
|
|
15502
|
-
var READ_ONLY_TAGS, PRODUCTION_READ_ONLY_GUARD_TEMPLATE, HOST_AGNOSTIC_URL_TEMPLATE, TEST_QUALITY_CONVENTIONS, NON_PROD_HOST_LABEL, NON_PROD_HOST_TOKEN, TRANSIENT_TEXT, STRING_LITERAL, FIRST_LITERAL, GET_BY_TEXT_LITERAL2, ROLE_NAME_LITERAL2, MAX_LINT_INPUT, MAX_BALANCED_SCAN, MAX_CALLS, MAX_SEEN_SCAN, ACTION_CALL, BY_NAME_LOCATOR, ACTION_AFTER, MAX_CONTACTS, MAX_SNAPSHOT_SCAN, PHONE_IN_TEXT, EMAIL_IN_TEXT, TEL_HREF, MAILTO_HREF, FIELD_LINE, INBOX_EMAIL_READ, MAIL_READ_CALL, RESERVED_EMAIL_TLD, PHONE_LABEL, NON_PHONE_NUMBER_LABEL, IDENTITY_LABEL, LOOKUP_LABEL, NON_NAME_SUFFIX_LABEL, DATE_LIKE, SIGNUP_ACTION_NAME, SIGNUP_SUBMIT_NAME, LOGIN_ACTION_NAME, SIGNUP_GOTO, LOGIN_GOTO, MINTED_INBOX_ALIAS, STAMP_EVAL, RECORDING_VALUE_PLACEHOLDERS, NEGATIVE_TEST_TITLE, NEGATIVE_SIGNUP_ASSERT, STORED_LOGIN_RE, CONFIRM_CLICK_RE, SAVE_UPDATE_RE, NOT_A_CONFIRM_RE, ENTITY_WORDS, CREATE_ENTITY_CLICK_RE, RESTORE_CLICK_RE, MAX_ACCOUNT_ACTIONS, MAX_TRY_BLOCKS, MAX_STATEMENT_LOOKBACK, MAX_FINDINGS5, MAX_TRY_BODY, AUTH_REGION_START, AUTH_REGION_END, INBOX_REGION_START, INBOX_REGION_END, ACCOUNT_ROUTE_RE, ENTITY_WORD_RES, PERSIST_CALL_RE, FILL_IDENT_ARG_RE, PERSIST_ARGS_RE, HOST_IN_URL_ARG, URL_WAIT_TOKEN, GLOB_ESCAPED, URL_WAIT_MAX_PATH, URL_WAIT_MAX_GLOB_STARS, UNREACHED_MAX_PATHS, UNREACHED_MATCH_BUDGET, STALE_TOKEN, RESULT_WAIT, WRITE_CALL_PRESENT, SUBMIT_CALL_PRESENT, TEST_CALL, VALUE_WRITE_CALLS, SUBMIT_CALLS, DISTINCT_PAGE_CALLS, OWN_CONTROL_MATCHERS, OUTCOME_CONTENT_MATCHERS, OUTCOME_STATE_MATCHERS, ANY_CONTENT_ARG, ROOT_SUBJECT, LITERAL_SUBJECT, MATCHER_AFTER_EXPECT, STALE_MAX_EVENTS, CHECKED, UNCHECKED, squash, NOT_JUDGED, STALE_REASON_TEXT, LIVE_WRITE_NAME, BUNDLED_CHECKS_BEFORE_WRITE, STOP, SYNONYMS, AREA_TOKENS;
|
|
16628
|
+
var READ_ONLY_TAGS, PRODUCTION_READ_ONLY_GUARD_TEMPLATE, HOST_AGNOSTIC_URL_TEMPLATE, TEST_QUALITY_CONVENTIONS, NON_PROD_HOST_LABEL, NON_PROD_HOST_TOKEN, TRANSIENT_TEXT, STRING_LITERAL, FIRST_LITERAL, GET_BY_TEXT_LITERAL2, ROLE_NAME_LITERAL2, MAX_LINT_INPUT, MAX_BALANCED_SCAN, MAX_CALLS, MAX_SEEN_SCAN, ACTION_CALL, BY_NAME_LOCATOR, ACTION_AFTER, MAX_CONTACTS, MAX_SNAPSHOT_SCAN, PHONE_IN_TEXT, EMAIL_IN_TEXT, TEL_HREF, MAILTO_HREF, FIELD_LINE, INBOX_EMAIL_READ, MAIL_READ_CALL, RESERVED_EMAIL_TLD, PHONE_LABEL, NON_PHONE_NUMBER_LABEL, IDENTITY_LABEL, LOOKUP_LABEL, NON_NAME_SUFFIX_LABEL, DATE_LIKE, SIGNUP_ACTION_NAME, SIGNUP_SUBMIT_NAME, LOGIN_ACTION_NAME, SIGNUP_GOTO, LOGIN_GOTO, MINTED_INBOX_ALIAS, STAMP_EVAL, RECORDING_VALUE_PLACEHOLDERS, NEGATIVE_TEST_TITLE, NEGATIVE_SIGNUP_ASSERT, STORED_LOGIN_RE, CONFIRM_CLICK_RE, SAVE_UPDATE_RE, NOT_A_CONFIRM_RE, ENTITY_WORDS, CREATE_ENTITY_CLICK_RE, RESTORE_CLICK_RE, MAX_ACCOUNT_ACTIONS, MAX_TRY_BLOCKS, MAX_STATEMENT_LOOKBACK, MAX_FINDINGS5, MAX_TRY_BODY, AUTH_REGION_START, AUTH_REGION_END, INBOX_REGION_START, INBOX_REGION_END, ACCOUNT_ROUTE_RE, ENTITY_WORD_RES, PERSIST_CALL_RE, FILL_IDENT_ARG_RE, PERSIST_ARGS_RE, HOST_IN_URL_ARG, URL_WAIT_TOKEN, GLOB_ESCAPED, URL_WAIT_MAX_PATH, URL_WAIT_MAX_GLOB_STARS, UNREACHED_MAX_PATHS, UNREACHED_MATCH_BUDGET, STALE_TOKEN, RESULT_WAIT, WRITE_CALL_PRESENT, SUBMIT_CALL_PRESENT, TEST_CALL, VALUE_WRITE_CALLS, SUBMIT_CALLS, DISTINCT_PAGE_CALLS, OWN_CONTROL_MATCHERS, OUTCOME_CONTENT_MATCHERS, OUTCOME_STATE_MATCHERS, ANY_CONTENT_ARG, ROOT_SUBJECT, LITERAL_SUBJECT, MATCHER_AFTER_EXPECT, STALE_MAX_EVENTS, CHECKED, UNCHECKED, squash, NOT_JUDGED, STALE_REASON_TEXT, LIVE_WRITE_NAME, BUNDLED_CHECKS_BEFORE_WRITE, MAX_REGEX_MATCHER_FINDINGS, REGEX_EQUALITY_MATCHERS, STOP, SYNONYMS, AREA_TOKENS;
|
|
15503
16629
|
var init_test_authoring_quality = __esm({
|
|
15504
16630
|
"../shared/dist/test-authoring-quality.js"() {
|
|
15505
16631
|
"use strict";
|
|
@@ -15731,6 +16857,8 @@ var init_test_authoring_quality = __esm({
|
|
|
15731
16857
|
};
|
|
15732
16858
|
LIVE_WRITE_NAME = /\b(?:submit|place|book|reserve|confirm|pay|send|checkout|order|register|sign\s?up|create account)\b/i;
|
|
15733
16859
|
BUNDLED_CHECKS_BEFORE_WRITE = 5;
|
|
16860
|
+
MAX_REGEX_MATCHER_FINDINGS = 10;
|
|
16861
|
+
REGEX_EQUALITY_MATCHERS = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual", "toContain", "toHaveJSProperty"]);
|
|
15734
16862
|
STOP = /* @__PURE__ */ new Set([
|
|
15735
16863
|
"a",
|
|
15736
16864
|
"an",
|
|
@@ -15969,7 +17097,7 @@ function normalizeBugPath(route) {
|
|
|
15969
17097
|
if (hash >= 0 && path[hash + 1] !== "/")
|
|
15970
17098
|
path = path.slice(0, hash);
|
|
15971
17099
|
const segments = path.toLowerCase().split("/").map((segment) => /\d/.test(segment) ? ":id" : segment);
|
|
15972
|
-
const joined = segments.join("/")
|
|
17100
|
+
const joined = stripTrailingSlashes(segments.join("/"));
|
|
15973
17101
|
return joined || (path.startsWith("/") ? "/" : "");
|
|
15974
17102
|
}
|
|
15975
17103
|
function isGenericConsoleMessage(message) {
|
|
@@ -15982,14 +17110,17 @@ function requestSignatures(text) {
|
|
|
15982
17110
|
const statuses = [...text.matchAll(HTTP_STATUS)].map((m) => ({ status: m[1], at: m.index ?? 0, end: (m.index ?? 0) + m[0].length }));
|
|
15983
17111
|
if (statuses.length === 0)
|
|
15984
17112
|
return [];
|
|
15985
|
-
const paths = [...text.matchAll(PATH_IN_TEXT)].map((m) =>
|
|
15986
|
-
|
|
15987
|
-
|
|
15988
|
-
|
|
15989
|
-
|
|
15990
|
-
|
|
15991
|
-
|
|
15992
|
-
|
|
17113
|
+
const paths = [...text.matchAll(PATH_IN_TEXT)].map((m) => {
|
|
17114
|
+
const rawPath = stripTrailingChars(m[2] ?? m[3] ?? "", PATH_TRAILING_PUNCTUATION);
|
|
17115
|
+
return {
|
|
17116
|
+
method: m[1] ?? null,
|
|
17117
|
+
path: normalizeBugPath(rawPath),
|
|
17118
|
+
// Before normalisation, which turns a segment with a digit (favicon-32x32.png) into :id.
|
|
17119
|
+
incidental: isIncidentalAssetPath(rawPath.split(/[?#]/, 1)[0].toLowerCase()),
|
|
17120
|
+
at: m.index ?? 0,
|
|
17121
|
+
end: (m.index ?? 0) + m[0].length
|
|
17122
|
+
};
|
|
17123
|
+
}).filter((p) => p.path.length > 1);
|
|
15993
17124
|
const out = /* @__PURE__ */ new Map();
|
|
15994
17125
|
for (const { status, at, end } of statuses) {
|
|
15995
17126
|
const lead = STATUS_TO_PATH.exec(text.slice(end, end + 40));
|
|
@@ -16099,10 +17230,12 @@ function dedupeSiteWideSuspectedBugs(drafts) {
|
|
|
16099
17230
|
delete plans.suspectedBugs;
|
|
16100
17231
|
}
|
|
16101
17232
|
}
|
|
16102
|
-
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;
|
|
17233
|
+
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;
|
|
16103
17234
|
var init_survey_bug_signature = __esm({
|
|
16104
17235
|
"../shared/dist/survey-bug-signature.js"() {
|
|
16105
17236
|
"use strict";
|
|
17237
|
+
init_trailing_slashes();
|
|
17238
|
+
PATH_TRAILING_PUNCTUATION = /[.,;:]/;
|
|
16106
17239
|
SIGNATURE_SOURCE_MAX_CHARS = 800;
|
|
16107
17240
|
UUID = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/g;
|
|
16108
17241
|
PREFIXED_ID = /\b[a-z]{1,8}[-_]\d+\b/g;
|
|
@@ -17383,9 +18516,9 @@ function lastLocatorLabels(chain) {
|
|
|
17383
18516
|
if (t)
|
|
17384
18517
|
labels.push(t[1]);
|
|
17385
18518
|
}
|
|
17386
|
-
const
|
|
17387
|
-
if (
|
|
17388
|
-
labels.push(
|
|
18519
|
+
const has2 = optionText(last.args, HAS_TEXT_OPTION_RE);
|
|
18520
|
+
if (has2 !== null)
|
|
18521
|
+
labels.push(has2);
|
|
17389
18522
|
}
|
|
17390
18523
|
return labels.map((l) => l.replace(/^[\s^(?:]+/, "").trim());
|
|
17391
18524
|
}
|
|
@@ -17433,7 +18566,7 @@ function pathIdsIn(text) {
|
|
|
17433
18566
|
function findCounterIdPins(code) {
|
|
17434
18567
|
if (code.length > CLOCK_MAX_CODE_CHARS)
|
|
17435
18568
|
return [];
|
|
17436
|
-
const
|
|
18569
|
+
const clean3 = stripLineComments(blankBlockComments3(code));
|
|
17437
18570
|
const fillValues = [];
|
|
17438
18571
|
const candidates = [];
|
|
17439
18572
|
let seenWrite = false;
|
|
@@ -17441,24 +18574,24 @@ function findCounterIdPins(code) {
|
|
|
17441
18574
|
CALL_RE.lastIndex = 0;
|
|
17442
18575
|
let m;
|
|
17443
18576
|
let calls = 0;
|
|
17444
|
-
while (calls++ < MAX_CALLS2 && (m = CALL_RE.exec(
|
|
18577
|
+
while (calls++ < MAX_CALLS2 && (m = CALL_RE.exec(clean3)) !== null) {
|
|
17445
18578
|
const name = m[1];
|
|
17446
18579
|
const open = m.index + m[0].length - 1;
|
|
17447
18580
|
if (VALUE_WRITES.has(name)) {
|
|
17448
18581
|
seenWrite = true;
|
|
17449
|
-
const args2 = argsAt(
|
|
18582
|
+
const args2 = argsAt(clean3, open);
|
|
17450
18583
|
if (args2 !== null)
|
|
17451
18584
|
fillValues.push(...staticStrings(args2));
|
|
17452
18585
|
continue;
|
|
17453
18586
|
}
|
|
17454
18587
|
if (createIndex === -1) {
|
|
17455
|
-
if (name === "click" && seenWrite && isCreateClick(
|
|
18588
|
+
if (name === "click" && seenWrite && isCreateClick(clean3, m.index))
|
|
17456
18589
|
createIndex = m.index;
|
|
17457
18590
|
continue;
|
|
17458
18591
|
}
|
|
17459
18592
|
if (name === "click")
|
|
17460
18593
|
continue;
|
|
17461
|
-
const args = argsAt(
|
|
18594
|
+
const args = argsAt(clean3, open);
|
|
17462
18595
|
if (args === null)
|
|
17463
18596
|
continue;
|
|
17464
18597
|
const isUrl = URL_SINKS.has(name);
|
|
@@ -17477,7 +18610,7 @@ function findCounterIdPins(code) {
|
|
|
17477
18610
|
}
|
|
17478
18611
|
if (createIndex === -1)
|
|
17479
18612
|
return [];
|
|
17480
|
-
const before =
|
|
18613
|
+
const before = clean3.slice(0, createIndex);
|
|
17481
18614
|
let pathIdsBefore = null;
|
|
17482
18615
|
const findings = [];
|
|
17483
18616
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -17501,7 +18634,7 @@ function findCounterIdPins(code) {
|
|
|
17501
18634
|
continue;
|
|
17502
18635
|
}
|
|
17503
18636
|
}
|
|
17504
|
-
findings.push({ kind: c.kind, literal: c.literal, line: lineAt(
|
|
18637
|
+
findings.push({ kind: c.kind, literal: c.literal, line: lineAt(clean3, c.index) });
|
|
17505
18638
|
}
|
|
17506
18639
|
return findings;
|
|
17507
18640
|
}
|
|
@@ -17796,7 +18929,7 @@ var init_suspicious_expectation = __esm({
|
|
|
17796
18929
|
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;
|
|
17797
18930
|
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;
|
|
17798
18931
|
STUCK_VETO_RE = new RegExp(`${NOT_STUCK_RE.source}|${LISTS_STUCK_RE.source}`, "i");
|
|
17799
|
-
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*)
|
|
18932
|
+
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;
|
|
17800
18933
|
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)";
|
|
17801
18934
|
ELLIPSIS = "(?:\\.{3}|\u2026)";
|
|
17802
18935
|
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;
|
|
@@ -17952,7 +19085,7 @@ function outcomeNamedByAssertion(outcome, code) {
|
|
|
17952
19085
|
function namedByStatements(outcome, statements) {
|
|
17953
19086
|
const text = outcomeText(outcome);
|
|
17954
19087
|
if (outcome.type === "url") {
|
|
17955
|
-
const routes = (text.match(ROUTE_TERM) ?? []).map((r) => r
|
|
19088
|
+
const routes = (text.match(ROUTE_TERM) ?? []).map((r) => stripTrailingChars(r, ROUTE_TRAILING_PUNCTUATION)).filter((r) => r.length > 1);
|
|
17956
19089
|
return statements.some((stmt) => {
|
|
17957
19090
|
if (!/\.(?:waitForURL|toHaveURL)\s{0,20}\(/.test(stmt))
|
|
17958
19091
|
return false;
|
|
@@ -18124,7 +19257,7 @@ function evaluatePromotionGate(input, checks = PROMOTION_QUALITY_CHECKS) {
|
|
|
18124
19257
|
...suspiciousTagged ? { suspiciousPass: { mode: suspicious, covered: suspiciousCovered, wouldHold: suspiciousHold } } : {}
|
|
18125
19258
|
};
|
|
18126
19259
|
}
|
|
18127
|
-
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;
|
|
19260
|
+
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;
|
|
18128
19261
|
var init_promotion_quality_gate = __esm({
|
|
18129
19262
|
"../shared/dist/promotion-quality-gate.js"() {
|
|
18130
19263
|
"use strict";
|
|
@@ -18139,17 +19272,21 @@ var init_promotion_quality_gate = __esm({
|
|
|
18139
19272
|
init_run_classification();
|
|
18140
19273
|
init_expected_outcome_coverage();
|
|
18141
19274
|
init_expect_call();
|
|
19275
|
+
init_trailing_slashes();
|
|
18142
19276
|
SOURCE_RANK = { generator: 0, classifier: 0, healer: 1, human: 2 };
|
|
18143
19277
|
strengthCheck = {
|
|
18144
19278
|
id: "assertion-strength",
|
|
18145
|
-
run({ code, strengthGate, flagContradictedAdditions }) {
|
|
18146
|
-
const verdict = validateAssertionStrength(code, { flagContradictedAdditions: flagContradictedAdditions === true });
|
|
19279
|
+
run({ code, tags, strengthGate, flagContradictedAdditions, lastActionProof }) {
|
|
19280
|
+
const verdict = validateAssertionStrength(code, { flagContradictedAdditions: flagContradictedAdditions === true, lastActionProof });
|
|
18147
19281
|
if (verdict.severity === "route-bug")
|
|
18148
19282
|
return [{ check: "route-bug" }];
|
|
18149
|
-
|
|
19283
|
+
const authDowngrade = verdict.severity === "downgrade" && tags.some((t) => t.toLowerCase() === "@auth");
|
|
19284
|
+
if (verdict.severity !== "block" && !authDowngrade)
|
|
18150
19285
|
return [];
|
|
18151
19286
|
const revealed = strengthGate?.severity === "none" && strengthGate.revealEvidence === "revealed" && strengthGate.source !== "heal";
|
|
18152
|
-
|
|
19287
|
+
if (revealed)
|
|
19288
|
+
return [];
|
|
19289
|
+
return [{ check: authDowngrade ? "strength-downgrade-auth" : "strength-block" }];
|
|
18153
19290
|
}
|
|
18154
19291
|
};
|
|
18155
19292
|
STRING_CONST = /\b(?:const|let|var)\s{1,20}([A-Za-z_$][\w$]{0,60})\s{0,20}=\s{0,20}(?:'([^'\\\n]{0,300})'|"([^"\\\n]{0,300})"|`([^`\\$\n]{0,300})`)\s{0,20}[;\n]/g;
|
|
@@ -18158,7 +19295,7 @@ var init_promotion_quality_gate = __esm({
|
|
|
18158
19295
|
MAX_STATEMENTS = 500;
|
|
18159
19296
|
MAX_STATEMENT_CHARS = 400;
|
|
18160
19297
|
LITERAL_OR_IDENTIFIER = /'(?:[^'\\\n]|\\.){0,500}'|"(?:[^"\\\n]|\\.){0,500}"|`(?:[^`\\]|\\.){0,2000}`|(?<![\w$.])[A-Za-z_$][\w$]{0,60}/g;
|
|
18161
|
-
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}\(/;
|
|
19298
|
+
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}\(/;
|
|
18162
19299
|
FEEDBACK_REGION = /getByRole\s{0,20}\(\s{0,20}['"`](?:alert|status)['"`]|\baria-live\b|\btoast|\bsnackbar\b|\bflash.?message\b/i;
|
|
18163
19300
|
ONE_SHOT_MESSAGE_REASON = "one-shot success/error banner text";
|
|
18164
19301
|
FEEDBACK_REGION_ALL = /getByRole\s{0,20}\(\s{0,20}['"`](?:alert|status)['"`]|\baria-live\b|\btoast|\bsnackbar\b|\bflash.?message\b/gi;
|
|
@@ -18217,6 +19354,7 @@ var init_promotion_quality_gate = __esm({
|
|
|
18217
19354
|
"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}\)/
|
|
18218
19355
|
};
|
|
18219
19356
|
ROUTE_TERM = /\/[A-Za-z0-9_./?&=#-]{1,200}/g;
|
|
19357
|
+
ROUTE_TRAILING_PUNCTUATION = /[.,;:!?)]/;
|
|
18220
19358
|
QUOTED_LITERAL = /'([^'\\\n]{3,200})'|"([^"\\\n]{3,200})"|`([^`\\$\n]{3,200})`/g;
|
|
18221
19359
|
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;
|
|
18222
19360
|
MAX_OUTCOME_STATEMENTS = 300;
|
|
@@ -18336,8 +19474,8 @@ ${goalText}`);
|
|
|
18336
19474
|
return count3 > 0 ? [{ check: "locator-fragment", detail: String(count3), soft: true }] : [];
|
|
18337
19475
|
}
|
|
18338
19476
|
};
|
|
18339
|
-
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;
|
|
18340
|
-
NEXT_ACTION = /\.(?:goto|reload|click|dblclick|tap|fill|type|pressSequentially|press|check|uncheck|selectOption|setInputFiles|dragTo|clear)\s*\(/;
|
|
19477
|
+
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;
|
|
19478
|
+
NEXT_ACTION = /\.(?:goto|reload|click|dblclick|tap|fill|type|pressSequentially|press|check|uncheck|setChecked|selectOption|setInputFiles|dragTo|dragAndDrop|clear)\s*\(/;
|
|
18341
19479
|
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;
|
|
18342
19480
|
MAX_FILTER_LINES = 2e3;
|
|
18343
19481
|
MAX_FILTER_LINE_CHARS = 600;
|
|
@@ -18452,7 +19590,7 @@ ${goalText}`);
|
|
|
18452
19590
|
});
|
|
18453
19591
|
|
|
18454
19592
|
// ../shared/dist/regex-alternation.js
|
|
18455
|
-
function
|
|
19593
|
+
function closingParens2(source) {
|
|
18456
19594
|
const close = new Int32Array(source.length).fill(-1);
|
|
18457
19595
|
const open = [];
|
|
18458
19596
|
let inClass = false;
|
|
@@ -18477,7 +19615,7 @@ function closingParens(source) {
|
|
|
18477
19615
|
return close;
|
|
18478
19616
|
}
|
|
18479
19617
|
function alternatesAtTop(source) {
|
|
18480
|
-
const close =
|
|
19618
|
+
const close = closingParens2(source);
|
|
18481
19619
|
let start = source.startsWith("^") ? 1 : 0;
|
|
18482
19620
|
let end = source.endsWith("$") ? source.length - 1 : source.length;
|
|
18483
19621
|
while (start < end && source[start] === "(" && close[start] === end - 1) {
|
|
@@ -18526,6 +19664,124 @@ var init_page_evidence = __esm({
|
|
|
18526
19664
|
}
|
|
18527
19665
|
});
|
|
18528
19666
|
|
|
19667
|
+
// ../shared/dist/stated-limits.js
|
|
19668
|
+
function validDate(v) {
|
|
19669
|
+
const [y, m, d] = v.split("-").map(Number);
|
|
19670
|
+
const t = new Date(Date.UTC(y, m - 1, d));
|
|
19671
|
+
return t.getUTCFullYear() === y && t.getUTCMonth() === m - 1 && t.getUTCDate() === d;
|
|
19672
|
+
}
|
|
19673
|
+
function isoFor(type, v) {
|
|
19674
|
+
const re = DATE_RE[type];
|
|
19675
|
+
if (!re || !re.test(v))
|
|
19676
|
+
return false;
|
|
19677
|
+
if (type === "date")
|
|
19678
|
+
return validDate(v);
|
|
19679
|
+
if (type === "datetime-local")
|
|
19680
|
+
return validDate(v.slice(0, 10)) && Number(v.slice(11, 13)) < 24 && Number(v.slice(14, 16)) < 60;
|
|
19681
|
+
if (type === "time")
|
|
19682
|
+
return Number(v.slice(0, 2)) < 24 && Number(v.slice(3, 5)) < 60;
|
|
19683
|
+
if (type === "month")
|
|
19684
|
+
return Number(v.slice(5, 7)) >= 1 && Number(v.slice(5, 7)) <= 12;
|
|
19685
|
+
return Number(v.slice(6, 8)) >= 1 && Number(v.slice(6, 8)) <= 53;
|
|
19686
|
+
}
|
|
19687
|
+
function clean2(raw) {
|
|
19688
|
+
return typeof raw === "string" ? raw.trim() : "";
|
|
19689
|
+
}
|
|
19690
|
+
function statedLimitsFromAttrs(attrs, inputType) {
|
|
19691
|
+
const type = (inputType ?? "").trim().toLowerCase();
|
|
19692
|
+
const limits = [];
|
|
19693
|
+
const add = (k, v) => {
|
|
19694
|
+
limits.push({ k, v });
|
|
19695
|
+
};
|
|
19696
|
+
if (NUMBER_TYPES.has(type)) {
|
|
19697
|
+
for (const [attr, k] of [["min", "min"], ["max", "max"]]) {
|
|
19698
|
+
const v = clean2(attrs[attr]);
|
|
19699
|
+
if (NUMBER_RE.test(v))
|
|
19700
|
+
add(k, v);
|
|
19701
|
+
}
|
|
19702
|
+
const step = clean2(attrs.step);
|
|
19703
|
+
if (NUMBER_RE.test(step) && Number(step) > 0)
|
|
19704
|
+
add("step", step);
|
|
19705
|
+
} else if (type in DATE_RE) {
|
|
19706
|
+
for (const [attr, k] of [["min", "min"], ["max", "max"]]) {
|
|
19707
|
+
const v = clean2(attrs[attr]);
|
|
19708
|
+
if (isoFor(type, v))
|
|
19709
|
+
add(k, v);
|
|
19710
|
+
}
|
|
19711
|
+
} else if (TEXT_TYPES.has(type)) {
|
|
19712
|
+
for (const [attr, k] of [["minlength", "minLength"], ["maxlength", "maxLength"]]) {
|
|
19713
|
+
const v = clean2(attrs[attr]);
|
|
19714
|
+
if (LENGTH_RE.test(v))
|
|
19715
|
+
add(k, String(Number(v)));
|
|
19716
|
+
}
|
|
19717
|
+
const pattern = cleanPattern(clean2(attrs.pattern).slice(0, STATED_PATTERN_MAX));
|
|
19718
|
+
if (pattern)
|
|
19719
|
+
add("pattern", pattern);
|
|
19720
|
+
}
|
|
19721
|
+
return limits.length > 0 ? { v: STATED_LIMITS_VERSION, limits } : null;
|
|
19722
|
+
}
|
|
19723
|
+
function cleanPattern(text) {
|
|
19724
|
+
return text.replace(/[\u0000-\u001f\u007f]/g, "").replace(/[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/g, "");
|
|
19725
|
+
}
|
|
19726
|
+
function readStatedLimits(stored) {
|
|
19727
|
+
if (!stored || typeof stored !== "object" || Array.isArray(stored))
|
|
19728
|
+
return [];
|
|
19729
|
+
const rec = stored;
|
|
19730
|
+
if (rec.v !== STATED_LIMITS_VERSION || !Array.isArray(rec.limits))
|
|
19731
|
+
return [];
|
|
19732
|
+
const out = [];
|
|
19733
|
+
const seen = /* @__PURE__ */ new Set();
|
|
19734
|
+
for (const item of rec.limits.slice(0, MAX_LIMITS * 2)) {
|
|
19735
|
+
if (!item || typeof item !== "object")
|
|
19736
|
+
continue;
|
|
19737
|
+
const { k, v } = item;
|
|
19738
|
+
if (typeof k !== "string" || typeof v !== "string" || !KINDS.includes(k) || seen.has(k))
|
|
19739
|
+
continue;
|
|
19740
|
+
if (v.length === 0 || v.length > STATED_PATTERN_MAX)
|
|
19741
|
+
continue;
|
|
19742
|
+
const text = k === "pattern" ? cleanPattern(v) : v;
|
|
19743
|
+
if (text === "")
|
|
19744
|
+
continue;
|
|
19745
|
+
if (k === "minLength" || k === "maxLength") {
|
|
19746
|
+
if (!LENGTH_RE.test(v))
|
|
19747
|
+
continue;
|
|
19748
|
+
} else if (k === "step") {
|
|
19749
|
+
if (!NUMBER_RE.test(v))
|
|
19750
|
+
continue;
|
|
19751
|
+
} else if (k === "min" || k === "max") {
|
|
19752
|
+
if (!NUMBER_RE.test(v) && !Object.values(DATE_RE).some((re) => re.test(v)))
|
|
19753
|
+
continue;
|
|
19754
|
+
}
|
|
19755
|
+
seen.add(k);
|
|
19756
|
+
out.push({ k, v: text });
|
|
19757
|
+
}
|
|
19758
|
+
return out;
|
|
19759
|
+
}
|
|
19760
|
+
function statedLimitsRecord(limits) {
|
|
19761
|
+
return limits.length > 0 ? { v: STATED_LIMITS_VERSION, limits } : null;
|
|
19762
|
+
}
|
|
19763
|
+
var STATED_LIMITS_VERSION, STATED_PATTERN_MAX, KINDS, MAX_LIMITS, TEXT_TYPES, NUMBER_TYPES, DATE_RE, NUMBER_RE, LENGTH_RE;
|
|
19764
|
+
var init_stated_limits = __esm({
|
|
19765
|
+
"../shared/dist/stated-limits.js"() {
|
|
19766
|
+
"use strict";
|
|
19767
|
+
STATED_LIMITS_VERSION = 1;
|
|
19768
|
+
STATED_PATTERN_MAX = 200;
|
|
19769
|
+
KINDS = ["min", "max", "minLength", "maxLength", "step", "pattern"];
|
|
19770
|
+
MAX_LIMITS = KINDS.length;
|
|
19771
|
+
TEXT_TYPES = /* @__PURE__ */ new Set(["text", "search", "email", "url", "tel", "password", "textarea", ""]);
|
|
19772
|
+
NUMBER_TYPES = /* @__PURE__ */ new Set(["number", "range"]);
|
|
19773
|
+
DATE_RE = {
|
|
19774
|
+
date: /^\d{4}-\d{2}-\d{2}$/,
|
|
19775
|
+
"datetime-local": /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?$/,
|
|
19776
|
+
time: /^\d{2}:\d{2}(?::\d{2})?$/,
|
|
19777
|
+
month: /^\d{4}-\d{2}$/,
|
|
19778
|
+
week: /^\d{4}-W\d{2}$/
|
|
19779
|
+
};
|
|
19780
|
+
NUMBER_RE = /^-?\d{1,15}(?:\.\d{1,10})?$/;
|
|
19781
|
+
LENGTH_RE = /^\d{1,7}$/;
|
|
19782
|
+
}
|
|
19783
|
+
});
|
|
19784
|
+
|
|
18529
19785
|
// ../shared/dist/index.js
|
|
18530
19786
|
var dist_exports = {};
|
|
18531
19787
|
__export(dist_exports, {
|
|
@@ -18533,9 +19789,11 @@ __export(dist_exports, {
|
|
|
18533
19789
|
ACTION_BATCH_MAX_SIZE: () => ACTION_BATCH_MAX_SIZE,
|
|
18534
19790
|
ADDON_DEFINITIONS: () => ADDON_DEFINITIONS,
|
|
18535
19791
|
ADMIN_EMAIL_LIMITS: () => ADMIN_EMAIL_LIMITS,
|
|
19792
|
+
ADMIN_FORCE_CANCELLED_RUN_ERROR: () => ADMIN_FORCE_CANCELLED_RUN_ERROR,
|
|
18536
19793
|
ADMIN_VIEW_DEFAULT_RETURN_TO: () => ADMIN_VIEW_DEFAULT_RETURN_TO,
|
|
18537
19794
|
AGENT_AI_PROVIDER_PREFIXES: () => AGENT_AI_PROVIDER_PREFIXES,
|
|
18538
19795
|
AI_CREDIT_MARGIN: () => AI_CREDIT_MARGIN,
|
|
19796
|
+
ALLOWED_PROVIDER_CHAIN_HOPS: () => ALLOWED_PROVIDER_CHAIN_HOPS,
|
|
18539
19797
|
API_AUTH_HEADERS_ENV_KEY: () => API_AUTH_HEADERS_ENV_KEY,
|
|
18540
19798
|
API_MODEL_CATALOG: () => API_MODEL_CATALOG,
|
|
18541
19799
|
API_MODEL_FIELDS: () => API_MODEL_FIELDS,
|
|
@@ -18556,6 +19814,7 @@ __export(dist_exports, {
|
|
|
18556
19814
|
AUDIO_CHUNK_DURATION_MS: () => AUDIO_CHUNK_DURATION_MS,
|
|
18557
19815
|
AUTH_PATH_SIGNATURE: () => AUTH_PATH_SIGNATURE,
|
|
18558
19816
|
AUTH_PROVIDER_HOST_SIGNATURES: () => AUTH_PROVIDER_HOST_SIGNATURES,
|
|
19817
|
+
AUTH_SESSION_GENERATION_RE: () => AUTH_SESSION_GENERATION_RE,
|
|
18559
19818
|
AUTH_STATE_EXPIRY_SKEW_MS: () => AUTH_STATE_EXPIRY_SKEW_MS,
|
|
18560
19819
|
AUTH_TAG_COOKIE_STATE: () => AUTH_TAG_COOKIE_STATE,
|
|
18561
19820
|
AUTH_TAG_LOGIN_BEARER: () => AUTH_TAG_LOGIN_BEARER,
|
|
@@ -18574,12 +19833,14 @@ __export(dist_exports, {
|
|
|
18574
19833
|
BUG_TEST_BROKEN_REASON: () => BUG_TEST_BROKEN_REASON,
|
|
18575
19834
|
BUG_TEST_BROKEN_TAG: () => BUG_TEST_BROKEN_TAG,
|
|
18576
19835
|
CACHE_PRICE_PER_M: () => CACHE_PRICE_PER_M,
|
|
19836
|
+
CANCELLED_REASON_PREFIXES: () => CANCELLED_REASON_PREFIXES,
|
|
18577
19837
|
CANVAS_UNSUPPORTED_BODY: () => CANVAS_UNSUPPORTED_BODY,
|
|
18578
19838
|
CANVAS_UNSUPPORTED_CODE: () => CANVAS_UNSUPPORTED_CODE,
|
|
18579
19839
|
CANVAS_UNSUPPORTED_NOT_CHARGED: () => CANVAS_UNSUPPORTED_NOT_CHARGED,
|
|
18580
19840
|
CANVAS_UNSUPPORTED_OVERRIDE_HINT: () => CANVAS_UNSUPPORTED_OVERRIDE_HINT,
|
|
18581
19841
|
CANVAS_UNSUPPORTED_TITLE: () => CANVAS_UNSUPPORTED_TITLE,
|
|
18582
19842
|
CAPTURE_JSON_STRUCTURE_LIMITS: () => CAPTURE_JSON_STRUCTURE_LIMITS,
|
|
19843
|
+
CHAIN_CANCELLED_RUN_ERROR: () => CHAIN_CANCELLED_RUN_ERROR,
|
|
18583
19844
|
CHAT_FAILOVER_KEY: () => CHAT_FAILOVER_KEY,
|
|
18584
19845
|
CLAUDE_CLI_DEFAULT_EFFORT: () => CLAUDE_CLI_DEFAULT_EFFORT,
|
|
18585
19846
|
CLAUDE_CLI_DEFAULT_MODEL: () => CLAUDE_CLI_DEFAULT_MODEL,
|
|
@@ -18616,6 +19877,7 @@ __export(dist_exports, {
|
|
|
18616
19877
|
DEFAULT_GEMINI_SCREEN_MODEL: () => DEFAULT_GEMINI_SCREEN_MODEL,
|
|
18617
19878
|
DEFAULT_JSON_STRUCTURE_LIMITS: () => DEFAULT_JSON_STRUCTURE_LIMITS,
|
|
18618
19879
|
DEFAULT_PLATFORM_DB_POOL_SIZE: () => DEFAULT_PLATFORM_DB_POOL_SIZE,
|
|
19880
|
+
DEFAULT_PROVIDER_ACCOUNT_ID: () => DEFAULT_PROVIDER_ACCOUNT_ID,
|
|
18619
19881
|
DEFAULT_SHARED_DB_POOL_SIZE: () => DEFAULT_SHARED_DB_POOL_SIZE,
|
|
18620
19882
|
DEFAULT_STT_MODEL: () => DEFAULT_STT_MODEL,
|
|
18621
19883
|
DEFAULT_STT_MODEL_BY_PROVIDER: () => DEFAULT_STT_MODEL_BY_PROVIDER,
|
|
@@ -18632,6 +19894,7 @@ __export(dist_exports, {
|
|
|
18632
19894
|
ENVIRONMENTAL_ERROR_STORED_PREFIXES: () => ENVIRONMENTAL_ERROR_STORED_PREFIXES,
|
|
18633
19895
|
EVIDENCE_NOTE_PREFIXES: () => EVIDENCE_NOTE_PREFIXES,
|
|
18634
19896
|
EXPECT_CALL_RE: () => EXPECT_CALL_RE,
|
|
19897
|
+
FEATURE_ROW_RUN_WINDOW: () => FEATURE_ROW_RUN_WINDOW,
|
|
18635
19898
|
FLOW_PHASE_LABELS: () => FLOW_PHASE_LABELS,
|
|
18636
19899
|
FLOW_PHASE_TYPES: () => FLOW_PHASE_TYPES,
|
|
18637
19900
|
FLOW_SERVER_TYPES: () => FLOW_SERVER_TYPES,
|
|
@@ -18688,6 +19951,8 @@ __export(dist_exports, {
|
|
|
18688
19951
|
LIVE_FRAME_STALE_MS: () => LIVE_FRAME_STALE_MS,
|
|
18689
19952
|
LIVE_FRAME_UNFOCUSED_INTERVAL_MS: () => LIVE_FRAME_UNFOCUSED_INTERVAL_MS,
|
|
18690
19953
|
LIVE_THUMBNAIL_MAX_BASE64_BYTES: () => LIVE_THUMBNAIL_MAX_BASE64_BYTES,
|
|
19954
|
+
MAX_BALANCED_SCAN: () => MAX_BALANCED_SCAN,
|
|
19955
|
+
MAX_CALLS: () => MAX_CALLS,
|
|
18691
19956
|
MAX_CREDIT_TOPUP_CENTS: () => MAX_CREDIT_TOPUP_CENTS,
|
|
18692
19957
|
MAX_DEEPLINK_LENGTH: () => MAX_DEEPLINK_LENGTH,
|
|
18693
19958
|
MAX_EMAIL_ADDRESS_LENGTH: () => MAX_EMAIL_ADDRESS_LENGTH,
|
|
@@ -18697,6 +19962,8 @@ __export(dist_exports, {
|
|
|
18697
19962
|
MAX_ORG_NAME_LENGTH: () => MAX_ORG_NAME_LENGTH,
|
|
18698
19963
|
MAX_PASSWORD_BYTES: () => MAX_PASSWORD_BYTES,
|
|
18699
19964
|
MAX_PLAYWRIGHT_CODE_LENGTH: () => MAX_PLAYWRIGHT_CODE_LENGTH,
|
|
19965
|
+
MAX_PROVIDER_ACCOUNTS: () => MAX_PROVIDER_ACCOUNTS,
|
|
19966
|
+
MAX_PROVIDER_ACCOUNT_MODELS: () => MAX_PROVIDER_ACCOUNT_MODELS,
|
|
18700
19967
|
MAX_SURVEY_ENTRY_URLS: () => MAX_SURVEY_ENTRY_URLS,
|
|
18701
19968
|
MAX_SURVEY_ENTRY_URL_LENGTH: () => MAX_SURVEY_ENTRY_URL_LENGTH,
|
|
18702
19969
|
MCP_ARMY_LIMITS: () => MCP_ARMY_LIMITS,
|
|
@@ -18732,6 +19999,7 @@ __export(dist_exports, {
|
|
|
18732
19999
|
MUSE_ENGINE_DEFAULT_MODEL: () => MUSE_ENGINE_DEFAULT_MODEL,
|
|
18733
20000
|
MUSE_ENGINE_MODELS: () => MUSE_ENGINE_MODELS,
|
|
18734
20001
|
NOREPLY_EMAIL: () => NOREPLY_EMAIL,
|
|
20002
|
+
OPENCODE_GO_DEEPSEEK_GLM_MODELS: () => OPENCODE_GO_DEEPSEEK_GLM_MODELS,
|
|
18735
20003
|
OPENCODE_GO_MODEL_PATTERN: () => OPENCODE_GO_MODEL_PATTERN,
|
|
18736
20004
|
OPENCODE_GO_PREFIX: () => OPENCODE_GO_PREFIX,
|
|
18737
20005
|
OPENROUTER_MAX_CALL_COST_USD: () => OPENROUTER_MAX_CALL_COST_USD,
|
|
@@ -18751,10 +20019,12 @@ __export(dist_exports, {
|
|
|
18751
20019
|
PLAN_FEATURES: () => PLAN_FEATURES,
|
|
18752
20020
|
PLAN_FEATURE_LOCKED_CODE: () => PLAN_FEATURE_LOCKED_CODE,
|
|
18753
20021
|
PLAN_ORDER: () => PLAN_ORDER,
|
|
20022
|
+
PLATFORM_ADMIN_CANCELLED_RUN_ERROR: () => PLATFORM_ADMIN_CANCELLED_RUN_ERROR,
|
|
18754
20023
|
PLATFORM_ADMIN_EMAIL: () => PLATFORM_ADMIN_EMAIL,
|
|
18755
20024
|
PLATFORM_DEFAULT_PROVIDER: () => PLATFORM_DEFAULT_PROVIDER,
|
|
18756
20025
|
PLATFORM_PLAN_BILLING: () => PLATFORM_PLAN_BILLING,
|
|
18757
20026
|
PLATFORM_SERVER_APPLICATION_NAME: () => PLATFORM_SERVER_APPLICATION_NAME,
|
|
20027
|
+
PLATFORM_SUPPORT_CANCELLED_RUN_ERROR: () => PLATFORM_SUPPORT_CANCELLED_RUN_ERROR,
|
|
18758
20028
|
POST_SPECIAL_TRIAL_DAYS: () => POST_SPECIAL_TRIAL_DAYS,
|
|
18759
20029
|
PRESET_DIRECT_FALLBACK: () => PRESET_DIRECT_FALLBACK,
|
|
18760
20030
|
PRODUCTION_READ_ONLY_GUARD_TEMPLATE: () => PRODUCTION_READ_ONLY_GUARD_TEMPLATE,
|
|
@@ -18766,17 +20036,25 @@ __export(dist_exports, {
|
|
|
18766
20036
|
PROJECT_OVERRIDE_FIELDS: () => PROJECT_OVERRIDE_FIELDS,
|
|
18767
20037
|
PROMOTION_GATE_MAX_CODE_CHARS: () => PROMOTION_GATE_MAX_CODE_CHARS,
|
|
18768
20038
|
PROMOTION_QUALITY_CHECKS: () => PROMOTION_QUALITY_CHECKS,
|
|
20039
|
+
PROVIDER_ACCOUNT_ID_PATTERN: () => PROVIDER_ACCOUNT_ID_PATTERN,
|
|
20040
|
+
PROVIDER_ACCOUNT_MODEL_PATTERN: () => PROVIDER_ACCOUNT_MODEL_PATTERN,
|
|
18769
20041
|
PROVIDER_CHAINS: () => PROVIDER_CHAINS,
|
|
20042
|
+
PROVIDER_CHAINS_SETTING_MAX_CHARS: () => PROVIDER_CHAINS_SETTING_MAX_CHARS,
|
|
20043
|
+
PROVIDER_CHAIN_LEADS: () => PROVIDER_CHAIN_LEADS,
|
|
20044
|
+
PROVIDER_CHAIN_MAX_ENTRIES: () => PROVIDER_CHAIN_MAX_ENTRIES,
|
|
20045
|
+
PROVIDER_HOST_NOT_OFFERED: () => PROVIDER_HOST_NOT_OFFERED,
|
|
18770
20046
|
PUBLIC_PLANS: () => PUBLIC_PLANS,
|
|
18771
20047
|
QUARANTINE_CLOSE_TAGS: () => QUARANTINE_CLOSE_TAGS,
|
|
18772
20048
|
READINESS_POLL_MAX_CODE_CHARS: () => READINESS_POLL_MAX_CODE_CHARS,
|
|
18773
20049
|
READ_ONLY_TAGS: () => READ_ONLY_TAGS,
|
|
18774
20050
|
RECHECKED_NOTES: () => RECHECKED_NOTES,
|
|
18775
20051
|
RECORDING_VALUE_PLACEHOLDERS: () => RECORDING_VALUE_PLACEHOLDERS,
|
|
20052
|
+
RENDER_IDENTITY_MAX_LENGTH: () => RENDER_IDENTITY_MAX_LENGTH,
|
|
18776
20053
|
REPOSITORY_CLIS: () => REPOSITORY_CLIS,
|
|
18777
20054
|
RESERVED_PROJECT_KEYS: () => RESERVED_PROJECT_KEYS,
|
|
18778
20055
|
RESULT_STRUCTURE_TOO_COSTLY_CODE: () => RESULT_STRUCTURE_TOO_COSTLY_CODE,
|
|
18779
20056
|
RETRIED_ATTEMPTS_KEY: () => RETRIED_ATTEMPTS_KEY,
|
|
20057
|
+
RETRY_HELD_RUN_ERROR: () => RETRY_HELD_RUN_ERROR,
|
|
18780
20058
|
RUNNER_INTERRUPT_ERROR_PREFIX: () => RUNNER_INTERRUPT_ERROR_PREFIX,
|
|
18781
20059
|
RUN_INTERRUPTED_ERROR_PREFIXES: () => RUN_INTERRUPTED_ERROR_PREFIXES,
|
|
18782
20060
|
RUN_INTERRUPTION_NOT_RETRIED_MARKERS: () => RUN_INTERRUPTION_NOT_RETRIED_MARKERS,
|
|
@@ -18789,6 +20067,8 @@ __export(dist_exports, {
|
|
|
18789
20067
|
SHARED_SERVER_APPLICATION_NAME: () => SHARED_SERVER_APPLICATION_NAME,
|
|
18790
20068
|
SPEND_SOURCE_KEY: () => SPEND_SOURCE_KEY,
|
|
18791
20069
|
SPEND_SOURCE_SERVER: () => SPEND_SOURCE_SERVER,
|
|
20070
|
+
STATED_LIMITS_VERSION: () => STATED_LIMITS_VERSION,
|
|
20071
|
+
STATED_PATTERN_MAX: () => STATED_PATTERN_MAX,
|
|
18792
20072
|
STATIC_STANDARD_MIN_INTERACTIVE: () => STATIC_STANDARD_MIN_INTERACTIVE,
|
|
18793
20073
|
STT_MODEL_OPTIONS: () => STT_MODEL_OPTIONS,
|
|
18794
20074
|
SUCCESS_NOTICE_TEXT: () => SUCCESS_NOTICE_TEXT,
|
|
@@ -18805,6 +20085,7 @@ __export(dist_exports, {
|
|
|
18805
20085
|
SURVEY_NOTES_RENDER_MAX_CHARS: () => SURVEY_NOTES_RENDER_MAX_CHARS,
|
|
18806
20086
|
SURVEY_NOTE_KEYS: () => SURVEY_NOTE_KEYS,
|
|
18807
20087
|
SUSPICIOUS_PASS_TAG: () => SUSPICIOUS_PASS_TAG,
|
|
20088
|
+
TEST_DID_NOT_RUN_ERROR: () => TEST_DID_NOT_RUN_ERROR,
|
|
18808
20089
|
TEST_QUALITY_CONVENTIONS: () => TEST_QUALITY_CONVENTIONS,
|
|
18809
20090
|
TEST_USER_ENV_KEYS: () => TEST_USER_ENV_KEYS,
|
|
18810
20091
|
TEST_VARIABLE_SECRET_KEY_PATTERN: () => TEST_VARIABLE_SECRET_KEY_PATTERN,
|
|
@@ -18817,23 +20098,33 @@ __export(dist_exports, {
|
|
|
18817
20098
|
UI_VARIANTS: () => UI_VARIANTS,
|
|
18818
20099
|
UNEXPECTED_PASS_ERROR_PREFIX: () => UNEXPECTED_PASS_ERROR_PREFIX,
|
|
18819
20100
|
USER_CANCELLED_RUN_ERROR: () => USER_CANCELLED_RUN_ERROR,
|
|
20101
|
+
VISUAL_MASK_SELECTORS_MAX: () => VISUAL_MASK_SELECTORS_MAX,
|
|
20102
|
+
VISUAL_MASK_SELECTOR_FORBIDDEN: () => VISUAL_MASK_SELECTOR_FORBIDDEN,
|
|
20103
|
+
VISUAL_MASK_SELECTOR_MAX_LENGTH: () => VISUAL_MASK_SELECTOR_MAX_LENGTH,
|
|
20104
|
+
VISUAL_V1_IDENTITY: () => VISUAL_V1_IDENTITY,
|
|
18820
20105
|
VOICE_PREVIEW_SAMPLE: () => VOICE_PREVIEW_SAMPLE,
|
|
18821
20106
|
WEB_PROXY_CHAIN_HEADER: () => WEB_PROXY_CHAIN_HEADER,
|
|
18822
20107
|
WITHHELD_CREDENTIAL_TEXT: () => WITHHELD_CREDENTIAL_TEXT,
|
|
20108
|
+
ZAI_MODEL_PATTERN: () => ZAI_MODEL_PATTERN,
|
|
20109
|
+
ZAI_PREFIX: () => ZAI_PREFIX,
|
|
20110
|
+
accountServesModel: () => accountServesModel,
|
|
18823
20111
|
accumulateStatements: () => accumulateStatements,
|
|
18824
20112
|
adoptBrokerFailover: () => adoptBrokerFailover,
|
|
18825
20113
|
aiCostToCreditCents: () => aiCostToCreditCents,
|
|
20114
|
+
allowedProviderChainHosts: () => allowedProviderChainHosts,
|
|
18826
20115
|
alternatesAtTop: () => alternatesAtTop,
|
|
18827
20116
|
analyzeDerivedValues: () => analyzeDerivedValues,
|
|
18828
20117
|
analyzeMobileSelectorQuality: () => analyzeMobileSelectorQuality,
|
|
18829
20118
|
analyzePersistenceReread: () => analyzePersistenceReread,
|
|
18830
20119
|
analyzeReadinessPolling: () => analyzeReadinessPolling,
|
|
20120
|
+
analyzeRegexMatchers: () => analyzeRegexMatchers,
|
|
18831
20121
|
analyzeSharedAccountUse: () => analyzeSharedAccountUse,
|
|
18832
20122
|
analyzeStaleAfterSubmit: () => analyzeStaleAfterSubmit,
|
|
18833
20123
|
analyzeTestData: () => analyzeTestData,
|
|
18834
20124
|
analyzeUnreachedUrlWaits: () => analyzeUnreachedUrlWaits,
|
|
18835
20125
|
analyzeUrlWaits: () => analyzeUrlWaits,
|
|
18836
20126
|
analyzeWriteResponse: () => analyzeWriteResponse,
|
|
20127
|
+
androidResourceOwners: () => androidResourceOwners,
|
|
18837
20128
|
apiModelCardsFromConfig: () => apiModelCardsFromConfig,
|
|
18838
20129
|
apiModelsForProvider: () => apiModelsForProvider,
|
|
18839
20130
|
apiProviderModelLabel: () => apiProviderModelLabel,
|
|
@@ -18844,6 +20135,7 @@ __export(dist_exports, {
|
|
|
18844
20135
|
applyLaunchSpecialPlan: () => applyLaunchSpecialPlan,
|
|
18845
20136
|
asPlainExpect: () => asPlainExpect,
|
|
18846
20137
|
assertionOnlyCode: () => assertionOnlyCode,
|
|
20138
|
+
assertionStrengthHoistedReadsOn: () => assertionStrengthHoistedReadsOn,
|
|
18847
20139
|
assertionsReadVisibleContent: () => assertionsReadVisibleContent,
|
|
18848
20140
|
assessCodeOutcomeCoverage: () => assessCodeOutcomeCoverage,
|
|
18849
20141
|
assessTitleCoverage: () => assessTitleCoverage,
|
|
@@ -18927,6 +20219,7 @@ __export(dist_exports, {
|
|
|
18927
20219
|
deepgramSpeak: () => deepgramSpeak,
|
|
18928
20220
|
deepgramTranscribe: () => deepgramTranscribe,
|
|
18929
20221
|
defaultConfirmModel: () => defaultConfirmModel,
|
|
20222
|
+
defaultProviderChainEntries: () => defaultProviderChainEntries,
|
|
18930
20223
|
defaultScreenModel: () => defaultScreenModel,
|
|
18931
20224
|
defaultVoiceForTtsModel: () => defaultVoiceForTtsModel,
|
|
18932
20225
|
deriveAuthPlan: () => deriveAuthPlan,
|
|
@@ -18969,6 +20262,7 @@ __export(dist_exports, {
|
|
|
18969
20262
|
extractOutcomeSearchTerms: () => extractOutcomeSearchTerms,
|
|
18970
20263
|
featureForAiCallType: () => featureForAiCallType,
|
|
18971
20264
|
featureForMcpTool: () => featureForMcpTool,
|
|
20265
|
+
featureRowRunStatus: () => featureRowRunStatus,
|
|
18972
20266
|
findBracedClause: () => findBracedClause,
|
|
18973
20267
|
findCounterIdPins: () => findCounterIdPins,
|
|
18974
20268
|
findCredentialTextLeftover: () => findCredentialTextLeftover,
|
|
@@ -18991,6 +20285,7 @@ __export(dist_exports, {
|
|
|
18991
20285
|
formatPersistenceRereadAdvisory: () => formatPersistenceRereadAdvisory,
|
|
18992
20286
|
formatPoolBudgetLine: () => formatPoolBudgetLine,
|
|
18993
20287
|
formatReadinessPollAdvisory: () => formatReadinessPollAdvisory,
|
|
20288
|
+
formatRegexMatcherNote: () => formatRegexMatcherNote,
|
|
18994
20289
|
formatStaleAfterSubmitAdvisory: () => formatStaleAfterSubmitAdvisory,
|
|
18995
20290
|
formatSuspiciousExpectation: () => formatSuspiciousExpectation,
|
|
18996
20291
|
formatUrlWaitAdvisory: () => formatUrlWaitAdvisory,
|
|
@@ -19016,6 +20311,7 @@ __export(dist_exports, {
|
|
|
19016
20311
|
getPlanChangeType: () => getPlanChangeType,
|
|
19017
20312
|
getPlanDefinition: () => getPlanDefinition,
|
|
19018
20313
|
getPlanLabel: () => getPlanLabel,
|
|
20314
|
+
getProviderChainOverrides: () => getProviderChainOverrides,
|
|
19019
20315
|
getTrialDaysRemaining: () => getTrialDaysRemaining,
|
|
19020
20316
|
googleBrokerNavigationUrl: () => googleBrokerNavigationUrl,
|
|
19021
20317
|
googleBrokerUnavailableLine: () => googleBrokerUnavailableLine,
|
|
@@ -19032,9 +20328,11 @@ __export(dist_exports, {
|
|
|
19032
20328
|
hasOpenCodeGoPrefix: () => hasOpenCodeGoPrefix,
|
|
19033
20329
|
hasOpenRouterPrefix: () => hasOpenRouterPrefix,
|
|
19034
20330
|
hasPlatformLocator: () => hasPlatformLocator,
|
|
20331
|
+
hasProviderChainLeadsByDefault: () => hasProviderChainLeadsByDefault,
|
|
19035
20332
|
hasReadOnlyMarker: () => hasReadOnlyMarker,
|
|
19036
20333
|
hasSurveyNotes: () => hasSurveyNotes,
|
|
19037
20334
|
hasTestFailContract: () => hasTestFailContract,
|
|
20335
|
+
hasZaiPrefix: () => hasZaiPrefix,
|
|
19038
20336
|
healMobileTestSteps: () => healMobileTestSteps,
|
|
19039
20337
|
healthRunWhere: () => healthRunWhere,
|
|
19040
20338
|
heldCodeRecord: () => heldCodeRecord,
|
|
@@ -19056,8 +20354,11 @@ __export(dist_exports, {
|
|
|
19056
20354
|
isAuthTokenName: () => isAuthTokenName,
|
|
19057
20355
|
isBlockedEnvVar: () => isBlockedEnvVar,
|
|
19058
20356
|
isBrowserCliEngine: () => isBrowserCliEngine,
|
|
20357
|
+
isBrowserVersion: () => isBrowserVersion,
|
|
19059
20358
|
isBugTestBrokenError: () => isBugTestBrokenError,
|
|
20359
|
+
isCancelledReason: () => isCancelledReason,
|
|
19060
20360
|
isCanvasUnsupportedMessage: () => isCanvasUnsupportedMessage,
|
|
20361
|
+
isChainablePrimary: () => isChainablePrimary,
|
|
19061
20362
|
isCliEngineModelId: () => isCliEngineModelId,
|
|
19062
20363
|
isCliEngineSpec: () => isCliEngineSpec,
|
|
19063
20364
|
isCodeFactNote: () => isCodeFactNote,
|
|
@@ -19081,6 +20382,7 @@ __export(dist_exports, {
|
|
|
19081
20382
|
isIdentityMobileXpath: () => isIdentityMobileXpath,
|
|
19082
20383
|
isIdentityProviderSurface: () => isIdentityProviderSurface,
|
|
19083
20384
|
isIncidentalAssetPath: () => isIncidentalAssetPath,
|
|
20385
|
+
isIntentionalNegativeAuthTest: () => isIntentionalNegativeAuthTest,
|
|
19084
20386
|
isIpOrLocalhost: () => isIpOrLocalhost,
|
|
19085
20387
|
isKnownPageType: () => isKnownPageType,
|
|
19086
20388
|
isLaunchSpecialActive: () => isLaunchSpecialActive,
|
|
@@ -19108,6 +20410,7 @@ __export(dist_exports, {
|
|
|
19108
20410
|
isRunInterruptedError: () => isRunInterruptedError,
|
|
19109
20411
|
isRunnableStep: () => isRunnableStep,
|
|
19110
20412
|
isSafeDeepLink: () => isSafeDeepLink,
|
|
20413
|
+
isSafeMaskSelector: () => isSafeMaskSelector,
|
|
19111
20414
|
isSameRegistrableDomainHost: () => isSameRegistrableDomainHost,
|
|
19112
20415
|
isSelectableApiModel: () => isSelectableApiModel,
|
|
19113
20416
|
isSelfServePlan: () => isSelfServePlan,
|
|
@@ -19138,6 +20441,7 @@ __export(dist_exports, {
|
|
|
19138
20441
|
isValidSttProvider: () => isValidSttProvider,
|
|
19139
20442
|
isValidTtsModel: () => isValidTtsModel,
|
|
19140
20443
|
isValidTtsVoiceId: () => isValidTtsVoiceId,
|
|
20444
|
+
isZaiModelId: () => isZaiModelId,
|
|
19141
20445
|
jsEscapedForms: () => jsEscapedForms,
|
|
19142
20446
|
jsonStructureRefusalOf: () => jsonStructureRefusalOf,
|
|
19143
20447
|
jsonStructureVerify: () => jsonStructureVerify,
|
|
@@ -19170,6 +20474,7 @@ __export(dist_exports, {
|
|
|
19170
20474
|
normalizeFinishReason: () => normalizeFinishReason,
|
|
19171
20475
|
normalizeNoteText: () => normalizeNoteText,
|
|
19172
20476
|
normalizePageType: () => normalizePageType,
|
|
20477
|
+
normalizeProviderAccountModels: () => normalizeProviderAccountModels,
|
|
19173
20478
|
normalizeTtsModel: () => normalizeTtsModel,
|
|
19174
20479
|
normalizeTtsVoiceId: () => normalizeTtsVoiceId,
|
|
19175
20480
|
noteChatFailover: () => noteChatFailover,
|
|
@@ -19186,6 +20491,7 @@ __export(dist_exports, {
|
|
|
19186
20491
|
parseApiCardModelUpdate: () => parseApiCardModelUpdate,
|
|
19187
20492
|
parseAppTimezone: () => parseAppTimezone,
|
|
19188
20493
|
parseAuditGroupScopeId: () => parseAuditGroupScopeId,
|
|
20494
|
+
parseAuthSessionStamp: () => parseAuthSessionStamp,
|
|
19189
20495
|
parseBoolFlag: () => parseBoolFlag,
|
|
19190
20496
|
parseDbPoolSize: () => parseDbPoolSize,
|
|
19191
20497
|
parseDeepgramListenResponse: () => parseDeepgramListenResponse,
|
|
@@ -19193,13 +20499,18 @@ __export(dist_exports, {
|
|
|
19193
20499
|
parseGeminiModelsPayload: () => parseGeminiModelsPayload,
|
|
19194
20500
|
parseGoogleAuthorizeUrl: () => parseGoogleAuthorizeUrl,
|
|
19195
20501
|
parseGoogleGisRequestUrl: () => parseGoogleGisRequestUrl,
|
|
20502
|
+
parseLastActionProofMode: () => parseLastActionProofMode,
|
|
19196
20503
|
parseLimitParam: () => parseLimitParam,
|
|
19197
20504
|
parseOffsetParam: () => parseOffsetParam,
|
|
19198
20505
|
parsePageParam: () => parsePageParam,
|
|
19199
20506
|
parsePgIntSetting: () => parsePgIntSetting,
|
|
19200
20507
|
parseProjectEngineOverrides: () => parseProjectEngineOverrides,
|
|
19201
20508
|
parsePromotionGateMode: () => parsePromotionGateMode,
|
|
20509
|
+
parseProviderAccountLists: () => parseProviderAccountLists,
|
|
20510
|
+
parseProviderChainOverrides: () => parseProviderChainOverrides,
|
|
19202
20511
|
parseRecheckClearMode: () => parseRecheckClearMode,
|
|
20512
|
+
parseRenderIdentity: () => parseRenderIdentity,
|
|
20513
|
+
parseStoredProviderChainOverrides: () => parseStoredProviderChainOverrides,
|
|
19203
20514
|
parseSurveyEntryUrls: () => parseSurveyEntryUrls,
|
|
19204
20515
|
parseSuspiciousPassMode: () => parseSuspiciousPassMode,
|
|
19205
20516
|
parseTaskModelUpdate: () => parseTaskModelUpdate,
|
|
@@ -19229,13 +20540,20 @@ __export(dist_exports, {
|
|
|
19229
20540
|
profileKeyRoute: () => profileKeyRoute,
|
|
19230
20541
|
projectAppHosts: () => projectAppHosts,
|
|
19231
20542
|
providerChainFor: () => providerChainFor,
|
|
20543
|
+
providerChainHostOf: () => providerChainHostOf,
|
|
20544
|
+
providerChainHostProvider: () => providerChainHostProvider,
|
|
20545
|
+
providerChainLeadsFor: () => providerChainLeadsFor,
|
|
20546
|
+
providerChainModelKey: () => providerChainModelKey,
|
|
19232
20547
|
providerChainPrimariesForHop: () => providerChainPrimariesForHop,
|
|
20548
|
+
providerHostPlan: () => providerHostPlan,
|
|
20549
|
+
readBalanced: () => readBalanced,
|
|
19233
20550
|
readChatFailover: () => readChatFailover,
|
|
19234
20551
|
readChatReasoningTokens: () => readChatReasoningTokens,
|
|
19235
20552
|
readChatUsage: () => readChatUsage,
|
|
19236
20553
|
readEnabledEngines: () => readEnabledEngines,
|
|
19237
20554
|
readProjectEngineOverrides: () => readProjectEngineOverrides,
|
|
19238
20555
|
readRetriedAttempts: () => readRetriedAttempts,
|
|
20556
|
+
readStatedLimits: () => readStatedLimits,
|
|
19239
20557
|
readStaticPageCanvasSignals: () => readStaticPageCanvasSignals,
|
|
19240
20558
|
readSurveyEntryUrls: () => readSurveyEntryUrls,
|
|
19241
20559
|
readSurveyNotes: () => readSurveyNotes,
|
|
@@ -19248,6 +20566,7 @@ __export(dist_exports, {
|
|
|
19248
20566
|
regexLiteralEnd: () => regexLiteralEnd2,
|
|
19249
20567
|
regexOpensAfter: () => regexOpensAfter,
|
|
19250
20568
|
registrableDomain: () => registrableDomain,
|
|
20569
|
+
renderIdentity: () => renderIdentity,
|
|
19251
20570
|
renderSurveyNotesForPrompt: () => renderSurveyNotesForPrompt,
|
|
19252
20571
|
repairPageType: () => repairPageType,
|
|
19253
20572
|
replaceBlockComments: () => replaceBlockComments,
|
|
@@ -19295,11 +20614,14 @@ __export(dist_exports, {
|
|
|
19295
20614
|
selectorCandidatesForStep: () => selectorCandidatesForStep,
|
|
19296
20615
|
selectorForValue: () => selectorForValue,
|
|
19297
20616
|
setAgenticCliRuntime: () => setAgenticCliRuntime,
|
|
20617
|
+
setProviderChainOverrides: () => setProviderChainOverrides,
|
|
19298
20618
|
signupPlanFor: () => signupPlanFor,
|
|
19299
20619
|
simplifyMobileWanderingInPlace: () => simplifyMobileWanderingInPlace,
|
|
19300
20620
|
siteOf: () => siteOf,
|
|
19301
20621
|
specItemsFromTestPlans: () => specItemsFromTestPlans,
|
|
19302
20622
|
splitAnswerLines: () => splitAnswerLines,
|
|
20623
|
+
statedLimitsFromAttrs: () => statedLimitsFromAttrs,
|
|
20624
|
+
statedLimitsRecord: () => statedLimitsRecord,
|
|
19303
20625
|
stepAppliesToPlatform: () => stepAppliesToPlatform,
|
|
19304
20626
|
stepConsoleLines: () => stepConsoleLines,
|
|
19305
20627
|
stepIsDivergentForPlatform: () => stepIsDivergentForPlatform,
|
|
@@ -19311,7 +20633,11 @@ __export(dist_exports, {
|
|
|
19311
20633
|
stripCodeFactPrefix: () => stripCodeFactPrefix,
|
|
19312
20634
|
stripCoverageComments: () => stripCoverageComments,
|
|
19313
20635
|
stripEnvironmentalPrefix: () => stripEnvironmentalPrefix,
|
|
20636
|
+
stripFractionZeros: () => stripFractionZeros,
|
|
20637
|
+
stripLineComment: () => stripLineComment,
|
|
19314
20638
|
stripLoginScaffolding: () => stripLoginScaffolding,
|
|
20639
|
+
stripTrailingChars: () => stripTrailingChars,
|
|
20640
|
+
stripTrailingSlashes: () => stripTrailingSlashes,
|
|
19315
20641
|
strongerNeedsReviewSource: () => strongerNeedsReviewSource,
|
|
19316
20642
|
sttModelsForProvider: () => sttModelsForProvider,
|
|
19317
20643
|
summarizeHealth: () => summarizeHealth,
|
|
@@ -19328,10 +20654,14 @@ __export(dist_exports, {
|
|
|
19328
20654
|
ttsFallbackModel: () => ttsFallbackModel,
|
|
19329
20655
|
ttsProviderForModel: () => ttsProviderForModel,
|
|
19330
20656
|
unexpectedPassError: () => unexpectedPassError,
|
|
20657
|
+
v1RenderIdentity: () => v1RenderIdentity,
|
|
19331
20658
|
validateAppMapDocument: () => validateAppMapDocument,
|
|
19332
20659
|
validateAssertionStrength: () => validateAssertionStrength,
|
|
19333
20660
|
validateEngineSpec: () => validateEngineSpec,
|
|
20661
|
+
validateProviderChainEntries: () => validateProviderChainEntries,
|
|
20662
|
+
validateProviderChainsSetting: () => validateProviderChainsSetting,
|
|
19334
20663
|
validateTaskModelId: () => validateTaskModelId,
|
|
20664
|
+
visualBaselinesEnabled: () => visualBaselinesEnabled,
|
|
19335
20665
|
visualProDailyBudget: () => visualProDailyBudget,
|
|
19336
20666
|
voiceMatchesTtsModel: () => voiceMatchesTtsModel,
|
|
19337
20667
|
voicesForTtsModel: () => voicesForTtsModel,
|
|
@@ -19339,7 +20669,8 @@ __export(dist_exports, {
|
|
|
19339
20669
|
withReviewSource: () => withReviewSource,
|
|
19340
20670
|
withoutNotChargedClaim: () => withoutNotChargedClaim,
|
|
19341
20671
|
withoutServerOnlySpendFields: () => withoutServerOnlySpendFields,
|
|
19342
|
-
withoutSurveyNotes: () => withoutSurveyNotes
|
|
20672
|
+
withoutSurveyNotes: () => withoutSurveyNotes,
|
|
20673
|
+
zaiWireModel: () => zaiWireModel
|
|
19343
20674
|
});
|
|
19344
20675
|
var init_dist = __esm({
|
|
19345
20676
|
"../shared/dist/index.js"() {
|
|
@@ -19381,6 +20712,7 @@ var init_dist = __esm({
|
|
|
19381
20712
|
init_admin_view_return_to();
|
|
19382
20713
|
init_auth_plan();
|
|
19383
20714
|
init_auth_block_detector();
|
|
20715
|
+
init_auth_session_stamp();
|
|
19384
20716
|
init_auth_state_freshness();
|
|
19385
20717
|
init_skip_coverage_detector();
|
|
19386
20718
|
init_feature_coverage();
|
|
@@ -19392,11 +20724,14 @@ var init_dist = __esm({
|
|
|
19392
20724
|
init_deepgram_voices();
|
|
19393
20725
|
init_feature_flags();
|
|
19394
20726
|
init_env_flags();
|
|
20727
|
+
init_visual_compare_limits();
|
|
20728
|
+
init_visual_baseline_identity();
|
|
19395
20729
|
init_analytics_url();
|
|
19396
20730
|
init_agentic_cli_runtime();
|
|
19397
20731
|
init_api_model_catalog();
|
|
19398
20732
|
init_engine_spec();
|
|
19399
20733
|
init_provider_chains();
|
|
20734
|
+
init_provider_accounts();
|
|
19400
20735
|
init_project_engine_overrides();
|
|
19401
20736
|
init_ai_profiles();
|
|
19402
20737
|
init_openrouter_usage();
|
|
@@ -19437,6 +20772,8 @@ var init_dist = __esm({
|
|
|
19437
20772
|
init_promotion_quality_gate();
|
|
19438
20773
|
init_regex_alternation();
|
|
19439
20774
|
init_page_evidence();
|
|
20775
|
+
init_trailing_slashes();
|
|
20776
|
+
init_stated_limits();
|
|
19440
20777
|
}
|
|
19441
20778
|
});
|
|
19442
20779
|
|
|
@@ -19465,17 +20802,12 @@ export {
|
|
|
19465
20802
|
stepsNeedingReviewForPlatform,
|
|
19466
20803
|
parseBoolFlag,
|
|
19467
20804
|
decideHarPosted,
|
|
20805
|
+
deriveAuthPlan,
|
|
19468
20806
|
isConfigLikeReason,
|
|
19469
20807
|
classifyErrorMessage,
|
|
19470
20808
|
isAppOriginResolutionEnabled,
|
|
19471
20809
|
resolveCrossOriginConfigOverride,
|
|
19472
|
-
deriveAuthPlan,
|
|
19473
|
-
detectAuthBlock,
|
|
19474
|
-
detectApiColdSessionBlock,
|
|
19475
|
-
coldSessionConfigMessage,
|
|
19476
|
-
detectSkippedApiCoverage,
|
|
19477
20810
|
isSharedAuthInjectEnabled,
|
|
19478
|
-
isAuthReadinessGuardEnabled,
|
|
19479
20811
|
isRepositoryCli,
|
|
19480
20812
|
repositoryCliLabel,
|
|
19481
20813
|
dist_exports,
|