@validate.qa/runner 1.0.101 → 1.0.103

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.
Files changed (31) hide show
  1. package/README.md +3 -0
  2. package/dist/{chunk-SN3PN4G4.mjs → chunk-36AWDKPL.mjs} +6 -2
  3. package/dist/{chunk-CYWHPXNZ.mjs → chunk-7SH3COMQ.mjs} +1 -1
  4. package/dist/{chunk-BBTK6OX6.mjs → chunk-BECCGMU4.mjs} +383 -11
  5. package/dist/{chunk-4SZC5ZX3.mjs → chunk-CG75I3ZZ.mjs} +25477 -5374
  6. package/dist/{chunk-TPU42VH2.mjs → chunk-CMM4DOXC.mjs} +10 -5
  7. package/dist/{chunk-M3VZGHZQ.mjs → chunk-NNJYKR3O.mjs} +22 -11
  8. package/dist/{chunk-IGXGSDE3.mjs → chunk-OTKJZIOD.mjs} +1298 -384
  9. package/dist/{chunk-CO5KHIVX.mjs → chunk-TK5KEGNW.mjs} +1 -1
  10. package/dist/{chunk-FQUH2PU2.mjs → chunk-WVLBQDFP.mjs} +140 -7
  11. package/dist/{chunk-2OKZY3WN.mjs → chunk-YI3ZC2XI.mjs} +2 -2
  12. package/dist/{chunk-XZUUBMP3.mjs → chunk-YIM746WO.mjs} +1 -1
  13. package/dist/cli.js +140692 -118903
  14. package/dist/cli.mjs +362 -108
  15. package/dist/credential-mcp-server.js +1402 -350
  16. package/dist/credential-mcp-server.mjs +3 -3
  17. package/dist/discovery-mcp-server.js +1330 -401
  18. package/dist/discovery-mcp-server.mjs +4 -4
  19. package/dist/dist-2G7YTNH5.mjs +17 -0
  20. package/dist/inbox-mcp-server.js +1676 -629
  21. package/dist/inbox-mcp-server.mjs +3 -3
  22. package/dist/route-seed-worker.js +1244 -330
  23. package/dist/route-seed-worker.mjs +4 -4
  24. package/dist/run-write-guard-preload.js +1622 -332
  25. package/dist/run-write-guard-preload.mjs +4 -4
  26. package/dist/run-write-guard.js +1622 -332
  27. package/dist/run-write-guard.mjs +3 -3
  28. package/dist/spec-driver.js +1 -1
  29. package/dist/spec-driver.mjs +1 -1
  30. package/package.json +3 -2
  31. package/dist/dist-VTPBY2C4.mjs +0 -17
@@ -67537,163 +67537,6 @@ var require_verification_inbox = __commonJS({
67537
67537
  }
67538
67538
  });
67539
67539
 
67540
- // ../runner-core/dist/services/harness-init-script.js
67541
- var require_harness_init_script = __commonJS({
67542
- "../runner-core/dist/services/harness-init-script.js"(exports2) {
67543
- "use strict";
67544
- Object.defineProperty(exports2, "__esModule", { value: true });
67545
- exports2.HARNESS_INIT_SOURCE_URL = void 0;
67546
- exports2.withHarnessSourceUrl = withHarnessSourceUrl;
67547
- exports2.buildAnimationDisablingSource = buildAnimationDisablingSource;
67548
- exports2.isHarnessInitLocation = isHarnessInitLocation;
67549
- exports2.isHarnessInitErrorStack = isHarnessInitErrorStack;
67550
- exports2.isHarnessInitConsoleLine = isHarnessInitConsoleLine;
67551
- exports2.stripHarnessConsoleEntries = stripHarnessConsoleEntries;
67552
- var node_crypto_1 = require("crypto");
67553
- exports2.HARNESS_INIT_SOURCE_URL = `vqa-harness-init-${(0, node_crypto_1.randomBytes)(4).toString("hex")}.js`;
67554
- function withHarnessSourceUrl(content) {
67555
- return `${content}
67556
- //# sourceURL=${exports2.HARNESS_INIT_SOURCE_URL}`;
67557
- }
67558
- function buildAnimationDisablingSource(opts) {
67559
- return withHarnessSourceUrl(`(() => {
67560
- // Layer 1: CSS transitions and animations - instant, not removed
67561
- // (duration 0s preserves final state; 'none' would prevent keyframes from completing)
67562
- try {
67563
- let applied = false;
67564
- const apply = () => {
67565
- if (applied) return true;
67566
- const root = document.head || document.documentElement;
67567
- if (!root) return false;
67568
- const style = document.createElement('style');
67569
- style.textContent = '*, *::before, *::after { transition-duration: 0s !important; animation-duration: 0s !important; transition-delay: 0s !important; animation-delay: 0s !important; }';
67570
- root.appendChild(style);
67571
- applied = true;
67572
- return true;
67573
- };
67574
- if (!apply()) {
67575
- // The document has no root element yet (iframe, about:blank, empty document): wait for it.
67576
- let observer = null;
67577
- const stop = () => { try { if (observer) observer.disconnect(); } catch (e) {} };
67578
- const retry = () => { try { if (apply()) stop(); } catch (e) { stop(); } };
67579
- try {
67580
- observer = new MutationObserver(retry);
67581
- observer.observe(document, { childList: true });
67582
- } catch (e) {}
67583
- document.addEventListener('readystatechange', retry);
67584
- document.addEventListener('DOMContentLoaded', () => { retry(); stop(); }, { once: true });
67585
- }
67586
- } catch (e) {}
67587
- ${opts.hideWebdriver ? `
67588
- try {
67589
- Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
67590
- } catch (e) {}
67591
- ` : ""}
67592
- // Layer 2: Web Animations API - make instant instead of no-op
67593
- // (preserves animation completion callbacks that app code may depend on)
67594
- try {
67595
- const origAnimate = Element.prototype.animate;
67596
- if (typeof origAnimate === 'function') {
67597
- Element.prototype.animate = function (keyframes, options) {
67598
- if (typeof options === 'object' && options !== null) {
67599
- options = Object.assign({}, options, { duration: 0, delay: 0 });
67600
- } else {
67601
- options = 0; // covers number, undefined, and any other type
67602
- }
67603
- return origAnimate.call(this, keyframes, options);
67604
- };
67605
- }
67606
- } catch (e) {}
67607
- })();`);
67608
- }
67609
- function isHarnessInitLocation(url) {
67610
- return url === exports2.HARNESS_INIT_SOURCE_URL;
67611
- }
67612
- var MAX_MATCHED_LINE_CHARS = 2048;
67613
- var MAX_SCANNED_LINES = 200;
67614
- var STACK_FRAME_RE = /^\s*at\s+(?:[^()\s][^()]*\()?([^()\s]+):\d+:\d+\)?\s*$/;
67615
- function isHarnessInitErrorStack(stack) {
67616
- if (!stack)
67617
- return false;
67618
- const lines = stack.split("\n", MAX_SCANNED_LINES);
67619
- for (const line of lines) {
67620
- if (line.length > MAX_MATCHED_LINE_CHARS)
67621
- continue;
67622
- const match = STACK_FRAME_RE.exec(line);
67623
- if (match)
67624
- return match[1] === exports2.HARNESS_INIT_SOURCE_URL;
67625
- }
67626
- return false;
67627
- }
67628
- var MCP_CONSOLE_LINE_LOCATION_RE = / @ ([^\s@]+):\d+$/;
67629
- function isHarnessInitConsoleLine(line) {
67630
- const trimmed = line.trim();
67631
- if (trimmed.length > MAX_MATCHED_LINE_CHARS)
67632
- return false;
67633
- const match = MCP_CONSOLE_LINE_LOCATION_RE.exec(trimmed);
67634
- return match !== null && match[1] === exports2.HARNESS_INIT_SOURCE_URL;
67635
- }
67636
- var MCP_LEVEL_LINE_RE = /^\s*(?:-\s+)?\[([A-Za-z]+)\]/;
67637
- var MCP_FRAME_LINE_RE = /^\s+at\s/;
67638
- var MCP_TOTALS_RE = /^(\s*Total messages:\s*)(\d+)(\s*\(Errors:\s*)(\d+)(,\s*Warnings:\s*)(\d+)(\).*)$/;
67639
- var MCP_RETURNING_RE = /^(\s*Returning\s+)(\d+)(\s+messages\b.*)$/;
67640
- function stripHarnessConsoleEntries(text) {
67641
- if (!text.includes(exports2.HARNESS_INIT_SOURCE_URL))
67642
- return text;
67643
- const lines = text.split("\n");
67644
- const out = [];
67645
- let removed = 0;
67646
- let removedErrors = 0;
67647
- let removedWarnings = 0;
67648
- let i = 0;
67649
- while (i < lines.length) {
67650
- const line = lines[i];
67651
- const level = MCP_LEVEL_LINE_RE.exec(line);
67652
- if (level && isHarnessInitConsoleLine(line.replace(/^\s*-\s+/, ""))) {
67653
- removed++;
67654
- const kind = level[1].toLowerCase();
67655
- if (kind === "error")
67656
- removedErrors++;
67657
- else if (kind === "warning" || kind === "warn")
67658
- removedWarnings++;
67659
- i++;
67660
- continue;
67661
- }
67662
- if (MCP_FRAME_LINE_RE.test(line)) {
67663
- let end = i;
67664
- while (end < lines.length && MCP_FRAME_LINE_RE.test(lines[end]))
67665
- end++;
67666
- const frames = lines.slice(i, end);
67667
- if (isHarnessInitErrorStack(frames.join("\n"))) {
67668
- const previous = out[out.length - 1];
67669
- if (previous !== void 0 && previous.trim() !== "" && !/^\s*(?:#|Total messages|Returning)/.test(previous) && !MCP_LEVEL_LINE_RE.test(previous))
67670
- out.pop();
67671
- removed++;
67672
- removedErrors++;
67673
- } else {
67674
- out.push(...frames);
67675
- }
67676
- i = end;
67677
- continue;
67678
- }
67679
- out.push(line);
67680
- i++;
67681
- }
67682
- if (removed === 0)
67683
- return text;
67684
- return out.map((line) => {
67685
- const totals = MCP_TOTALS_RE.exec(line);
67686
- if (totals) {
67687
- const [, a, total, b, errors2, c, warnings, d] = totals;
67688
- return `${a}${Math.max(0, +total - removed)}${b}${Math.max(0, +errors2 - removedErrors)}${c}${Math.max(0, +warnings - removedWarnings)}${d}`;
67689
- }
67690
- const returning = MCP_RETURNING_RE.exec(line);
67691
- return returning ? `${returning[1]}${Math.max(0, +returning[2] - removed)}${returning[3]}` : line;
67692
- }).join("\n");
67693
- }
67694
- }
67695
- });
67696
-
67697
67540
  // ../shared/dist/types.js
67698
67541
  function isUIVariant(value) {
67699
67542
  return typeof value === "string" && UI_VARIANTS.includes(value);
@@ -67743,13 +67586,13 @@ function isBlockedEnvVar(key) {
67743
67586
  function sanitizeTestVariables(vars) {
67744
67587
  if (!vars)
67745
67588
  return {};
67746
- const clean2 = {};
67589
+ const clean3 = {};
67747
67590
  for (const [key, value] of Object.entries(vars)) {
67748
67591
  if (!isBlockedEnvVar(key)) {
67749
- clean2[key] = value;
67592
+ clean3[key] = value;
67750
67593
  }
67751
67594
  }
67752
- return clean2;
67595
+ return clean3;
67753
67596
  }
67754
67597
  function buildSandboxedEnv(processEnv, testVariables) {
67755
67598
  const env = {};
@@ -69206,9 +69049,40 @@ function semanticTarget(label, platform, base2) {
69206
69049
  }
69207
69050
  };
69208
69051
  }
69052
+ function androidResourceOwners(value) {
69053
+ const owners = [];
69054
+ for (const match of value.matchAll(ANDROID_RESOURCE_ID_SUFFIX_RE)) {
69055
+ let runStart = match.index;
69056
+ while (runStart > 0 && /[a-z0-9_.]/i.test(value[runStart - 1]))
69057
+ runStart--;
69058
+ const owner = ownerAtEndOfRun(value.slice(runStart, match.index));
69059
+ if (owner !== null)
69060
+ owners.push(owner);
69061
+ }
69062
+ return owners;
69063
+ }
69064
+ function ownerAtEndOfRun(run) {
69065
+ const segments = run.split(".");
69066
+ const last = segments.length - 1;
69067
+ const startsWithLetter = (segment) => /^[a-z]/i.test(segment);
69068
+ if (last < 1 || !startsWithLetter(segments[last]))
69069
+ return null;
69070
+ let first = last;
69071
+ while (first > 0 && startsWithLetter(segments[first - 1]))
69072
+ first--;
69073
+ let offset = 0;
69074
+ for (let i = 0; i < first - 1; i++)
69075
+ offset += segments[i].length + 1;
69076
+ if (first > 0) {
69077
+ const letterAt = segments[first - 1].search(/[a-z]/i);
69078
+ if (letterAt >= 0)
69079
+ return run.slice(offset + letterAt);
69080
+ offset += segments[first - 1].length + 1;
69081
+ }
69082
+ return first < last ? run.slice(offset) : null;
69083
+ }
69209
69084
  function containsForeignAppPackage(value, targetAppId) {
69210
- for (const match of value.matchAll(ANDROID_RESOURCE_OWNER_RE)) {
69211
- const owner = match[1];
69085
+ for (const owner of androidResourceOwners(value)) {
69212
69086
  if (targetAppId && owner !== targetAppId && !owner.startsWith(`${targetAppId}.`))
69213
69087
  return true;
69214
69088
  }
@@ -69345,7 +69219,7 @@ function healMobileTestSteps(input) {
69345
69219
  summary: changed ? uniq(summary).slice(0, 12) : []
69346
69220
  };
69347
69221
  }
69348
- var MOBILE_STEP_ACTIONS, FOREIGN_PACKAGE_RE, ANDROID_RESOURCE_OWNER_RE, COMMON_EXTERNAL_PREFIXES;
69222
+ var MOBILE_STEP_ACTIONS, FOREIGN_PACKAGE_RE, ANDROID_RESOURCE_ID_SUFFIX_RE, COMMON_EXTERNAL_PREFIXES;
69349
69223
  var init_mobile_step_healer = __esm({
69350
69224
  "../shared/dist/mobile-step-healer.js"() {
69351
69225
  "use strict";
@@ -69366,7 +69240,7 @@ var init_mobile_step_healer = __esm({
69366
69240
  "assertStateDelta"
69367
69241
  ]);
69368
69242
  FOREIGN_PACKAGE_RE = /\b[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*){2,}\b/gi;
69369
- ANDROID_RESOURCE_OWNER_RE = /([a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)+):id\//gi;
69243
+ ANDROID_RESOURCE_ID_SUFFIX_RE = /:id\//gi;
69370
69244
  COMMON_EXTERNAL_PREFIXES = [
69371
69245
  "com.android.",
69372
69246
  "com.google.",
@@ -71076,7 +70950,7 @@ var init_assertion_classifier = __esm({
71076
70950
  // toHaveCount(0) stays tier 4 (absence proof) via TIER4_PATTERNS.
71077
70951
  [/\.toHaveCount\s*\(\s*[1-9]\d*\s*\)/, "non-zero toHaveCount asserts a record count that changes between runs"],
71078
70952
  // parenthesised counts like "(3)" or "[12]" in text - but not Playwright method args
71079
- [/(?<!\.\w{3,20}\s*)\(\s*\d+\s*\)(?!\s*[,;]?\s*\))/i, "parenthesised count is session-specific"],
70953
+ [/\((?<!\.\w{3,20}\s*\()\s*\d+\s*\)(?!\s*(?:[,;]\s*)?\))/i, "parenthesised count is session-specific"],
71080
70954
  // bare email addresses in assertion content
71081
70955
  [/\b[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}\b/i, "email address is user-specific"],
71082
70956
  // currency/balance amounts (dynamic account data)
@@ -71322,7 +71196,7 @@ var init_assertion_proof = __esm({
71322
71196
  PRESENCE_MATCHER = /\.(?:toBeVisible|toBeAttached|toBeEnabled|toBeInViewport|toBeEditable|toBeFocused)\s*\(|\.toHaveCount\s*\(\s*1\s*\)/;
71323
71197
  TEXT_MATCHER = /\.(?:toHaveText|toContainText)\s*\(/;
71324
71198
  ANY_VALUE_SOURCE = /^(?:\.|\\S|\\w|\\d|\[\^\\s\]|\[0-9\]|\[\\d\]|\[\\w\]|\[\\s\\S\])[+*]?\??$/;
71325
- ANY_VALUE_MATCHER = /\.(?:toHaveValue|toHaveText|toContainText)\s*\(\s*\/((?:\\.|[^/\\]){1,300})\/[a-z]*\s*\)/;
71199
+ ANY_VALUE_MATCHER = /\.(?:toHaveValue|toHaveText|toContainText)\s*\(\s*\/((?:\\.|[^/\\]){1,300})\/[a-z]*\s*[,)]/;
71326
71200
  EMPTY_NOT_VALUE = /\.not\s*\.\s*toHaveValue\s*\(\s*(?:['"`]{2}|\/\^?\$?\/)/;
71327
71201
  TEXT_MATCHER_LITERAL = /\.(?:toHaveText|toContainText)\s*\(\s*(['"`])((?:(?!\1).){1,300})\1/;
71328
71202
  GET_BY_TEXT_LITERAL = /getByText\s*\(\s*(['"`])((?:(?!\1).){1,300})\1/;
@@ -71339,7 +71213,7 @@ var init_assertion_proof = __esm({
71339
71213
  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;
71340
71214
  TEXT_LOCATOR = /\.getByText\s*\(|\.filter\s*\(\s*\{\s*hasText\s*:/g;
71341
71215
  CSS_LOCATOR = /\.locator\s*\(\s*(['"`])((?:(?!\1).){1,300})\1/g;
71342
- SPECIFIC_CSS = /\[[^\]]*=|#[\w-]|has-text|:text/;
71216
+ SPECIFIC_CSS = /\[[^\][=]*=|#[\w-]|has-text|:text/;
71343
71217
  }
71344
71218
  });
71345
71219
 
@@ -72303,22 +72177,37 @@ function classifyAuthHandoffIntent(text) {
72303
72177
  function withoutIdpUrlAsserts(code) {
72304
72178
  return code.replace(/\.(?:toHaveURL|waitForURL)\s*\((?:[^)(]|\([^)]*\))*\)/g, (match) => /accountchooser|accounts\.google|appleid\.apple|login\.microsoftonline|\/v\d+\/signin|oauth/i.test(match) ? "" : match);
72305
72179
  }
72180
+ function stripLineComment(line) {
72181
+ let from = line.length;
72182
+ while (from > 0) {
72183
+ const c = line.charCodeAt(from - 1);
72184
+ if (c === 10 || c === 13 || c === 8232 || c === 8233)
72185
+ break;
72186
+ from--;
72187
+ }
72188
+ const at = line.indexOf("//", from);
72189
+ return at < 0 ? line : line.slice(0, at);
72190
+ }
72306
72191
  function stripComments(code) {
72307
- return replaceBlockComments(code, () => "").split("\n").map((line) => line.replace(/\/\/.*$/, "")).join("\n");
72192
+ return replaceBlockComments(code, () => "").split("\n").map(stripLineComment).join("\n");
72308
72193
  }
72309
- function balancedCall(code, start) {
72310
- const open = code.indexOf("(", start);
72311
- if (open < 0)
72312
- return null;
72313
- let depth = 0;
72314
- for (let i = open; i < code.length; i++) {
72194
+ function closingParens(code) {
72195
+ const close = new Int32Array(code.length).fill(-1);
72196
+ const open = [];
72197
+ for (let i = 0; i < code.length; i++) {
72315
72198
  const c = code[i];
72316
72199
  if (c === "(")
72317
- depth++;
72318
- else if (c === ")" && --depth === 0)
72319
- return { inner: code.slice(open + 1, i), end: i };
72200
+ open.push(i);
72201
+ else if (c === ")" && open.length > 0)
72202
+ close[open.pop()] = i;
72320
72203
  }
72321
- return null;
72204
+ return close;
72205
+ }
72206
+ function balancedCall(code, close, start) {
72207
+ const open = code.indexOf("(", start);
72208
+ if (open < 0 || close[open] < 0)
72209
+ return null;
72210
+ return { inner: code.slice(open + 1, close[open]), end: close[open] };
72322
72211
  }
72323
72212
  function resolveLocator(code, subject) {
72324
72213
  const id = subject.trim().match(/^([A-Za-z_$][\w$]*)$/);
@@ -72366,8 +72255,10 @@ function isAuthHandoffDismissCode(code) {
72366
72255
  const expects = [];
72367
72256
  const re = new RegExp(EXPECT_CALL_RE.source, "g");
72368
72257
  let match;
72258
+ let close = null;
72369
72259
  while (match = re.exec(body)) {
72370
- const found = balancedCall(body, match.index);
72260
+ close ??= closingParens(body);
72261
+ const found = balancedCall(body, close, match.index);
72371
72262
  if (found)
72372
72263
  expects.push({ inner: found.inner, tail: body.slice(found.end, found.end + 48) });
72373
72264
  }
@@ -72412,59 +72303,256 @@ var init_auth_handoff_coverage = __esm({
72412
72303
  }
72413
72304
  });
72414
72305
 
72415
- // ../shared/dist/assertion-strength.js
72416
- function liveExpectStatements(uncommented) {
72417
- const masked = maskedOutsideStrings(uncommented).split("\n");
72418
- return accumulateStatements(uncommented.split("\n")).filter((s) => EXPECT_STMT.test(s.stmt) && EXPECT_STMT.test(masked[s.startLine - 1] ?? ""));
72306
+ // ../shared/dist/auth-plan.js
72307
+ function hasInlineLoginSteps(playwrightCode) {
72308
+ if (!playwrightCode)
72309
+ return false;
72310
+ return /process\.env\.TEST_USER_EMAIL!?/.test(playwrightCode) && /process\.env\.TEST_USER_PASSWORD!?/.test(playwrightCode);
72419
72311
  }
72420
- function stripLineComment(ln) {
72421
- let quote = null;
72422
- for (let i = 0; i < ln.length; i++) {
72423
- const c = ln[i];
72424
- if (quote) {
72425
- if (c === quote && ln[i - 1] !== "\\")
72426
- quote = null;
72427
- continue;
72428
- }
72429
- if (c === '"' || c === "'" || c === "`") {
72430
- quote = c;
72431
- continue;
72432
- }
72433
- if (c === "/" && ln[i + 1] === "/")
72434
- return ln.slice(0, i);
72435
- }
72436
- return ln;
72312
+ function hasGeneratedAuthGuard(playwrightCode) {
72313
+ if (!playwrightCode)
72314
+ return false;
72315
+ return /\/\/ >>> VALIDATE_QA_AUTH v(\d+)\b[\s\S]*?process\.env\.AUTH_FILE[\s\S]*?\/\/ <<< VALIDATE_QA_AUTH v\1\b/.test(playwrightCode);
72437
72316
  }
72438
- function splitInteractionStatementsWithLines(lines) {
72439
- const out = [];
72440
- let depth = 0;
72441
- let scaffold = false;
72442
- for (let i = 0; i < lines.length; i++) {
72443
- const ln = stripLineComment(lines[i]).trim();
72444
- if (!ln)
72445
- continue;
72446
- const isContinuation = out.length > 0 && (ln.startsWith(".") || depth > 0 && !scaffold);
72447
- if (isContinuation) {
72448
- out[out.length - 1].stmt += " " + ln;
72449
- depth += parenDepthDelta(ln);
72450
- } else {
72451
- out.push({ stmt: ln, startLine: i + 1 });
72452
- depth = parenDepthDelta(ln);
72453
- scaffold = BLOCK_SCAFFOLD2.test(ln);
72454
- }
72455
- if (depth < 0)
72456
- depth = 0;
72317
+ function legacyNeedsCookieState(tags, code, sharedAuthInjectEnabled) {
72318
+ if (!sharedAuthInjectEnabled)
72319
+ return false;
72320
+ if (tags.includes("@auth"))
72321
+ return false;
72322
+ if (tags.includes("@api"))
72323
+ return true;
72324
+ if (!tags.includes("@needs-auth"))
72325
+ return false;
72326
+ if (sharedAuthInjectEnabled && tags.includes("@auth-guarded") && hasGeneratedAuthGuard(code))
72327
+ return true;
72328
+ const isGeneratedVariant = tags.includes("@edge") || tags.includes("@bug");
72329
+ const hasLegacyInlineLogin = tags.includes("@discovered") && !isGeneratedVariant && hasInlineLoginSteps(code);
72330
+ return !tags.includes("@inline-login") && !hasLegacyInlineLogin;
72331
+ }
72332
+ function codeReadsStoredCredentials(code) {
72333
+ if (!code)
72334
+ return false;
72335
+ if (/__vqaLogin\b/.test(code))
72336
+ return true;
72337
+ for (const m of code.matchAll(/TEST_[A-Z0-9_]+/g)) {
72338
+ if (/_(?:EMAIL|PASSWORD)/.test(m[0]))
72339
+ return true;
72457
72340
  }
72458
- return out;
72341
+ for (const m of code.matchAll(/process\.env\s*\[([^\]]*)\]?/g)) {
72342
+ if (!/^\s*(['"])[^'"`]*\1\s*$/.test(m[1]))
72343
+ return true;
72344
+ }
72345
+ return false;
72459
72346
  }
72460
- function testBlockIds(lines) {
72461
- const ids = new Array(lines.length).fill(0);
72462
- let depth = 0;
72463
- let open = false;
72464
- let any = false;
72465
- let block = 0;
72347
+ function explicitAuthTypeTags(t) {
72348
+ const found = /* @__PURE__ */ new Set();
72349
+ for (const tag of t) {
72350
+ if (AUTH_TYPE_TAGS.includes(tag))
72351
+ found.add(tag);
72352
+ else if (Object.hasOwn(AUTH_TAG_ALIASES, tag))
72353
+ found.add(AUTH_TAG_ALIASES[tag]);
72354
+ }
72355
+ return [...found];
72356
+ }
72357
+ function invalidPlan(configurationError) {
72358
+ return {
72359
+ version: 1,
72360
+ source: "invalid",
72361
+ acquisition: "unsupported",
72362
+ presentation: "none",
72363
+ needsCookieState: false,
72364
+ needsStandingHeaders: false,
72365
+ standingHeadersMode: "none",
72366
+ needsLoginCreds: false,
72367
+ requiredEnvKeys: [],
72368
+ configurationError
72369
+ };
72370
+ }
72371
+ function explicitPlan(authTypeTag2, acquisition, presentation, opts) {
72372
+ return {
72373
+ version: 1,
72374
+ source: "explicit",
72375
+ authTypeTag: authTypeTag2,
72376
+ acquisition,
72377
+ presentation,
72378
+ needsCookieState: opts.cookie,
72379
+ needsStandingHeaders: opts.standingMode === "required",
72380
+ standingHeadersMode: opts.standingMode,
72381
+ needsLoginCreds: TEST_USER_ENV_KEYS.every((k) => opts.requiredEnvKeys.includes(k)),
72382
+ requiredEnvKeys: opts.requiredEnvKeys
72383
+ };
72384
+ }
72385
+ function deriveAuthPlan(tags, code, opts) {
72386
+ const t = Array.isArray(tags) ? tags : [];
72387
+ const sharedFlag = opts?.sharedAuthInjectEnabled ?? false;
72388
+ const isApi = t.includes("@api");
72389
+ const explicit = isApi ? explicitAuthTypeTags(t) : [];
72390
+ if (explicit.length > 0) {
72391
+ if (t.includes("@auth")) {
72392
+ return invalidPlan("@auth (self-login UI) cannot be combined with an explicit @api auth-type tag");
72393
+ }
72394
+ const loginCompose = explicit.filter((e) => e === AUTH_TAG_COOKIE_STATE || e === AUTH_TAG_LOGIN_COOKIE || e === AUTH_TAG_LOGIN_BEARER);
72395
+ if (explicit.length === 2 && explicit.includes(AUTH_TAG_STANDING) && loginCompose.length === 1) {
72396
+ const composedKeys = [...TEST_USER_ENV_KEYS, API_AUTH_HEADERS_ENV_KEY];
72397
+ switch (loginCompose[0]) {
72398
+ case AUTH_TAG_COOKIE_STATE:
72399
+ return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "required", requiredEnvKeys: composedKeys });
72400
+ case AUTH_TAG_LOGIN_COOKIE:
72401
+ return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
72402
+ case AUTH_TAG_LOGIN_BEARER:
72403
+ return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
72404
+ }
72405
+ }
72406
+ if (explicit.length > 1) {
72407
+ return invalidPlan(`conflicting auth-type tags: ${explicit.join(", ")}`);
72408
+ }
72409
+ switch (explicit[0]) {
72410
+ case AUTH_TAG_UNSUPPORTED:
72411
+ return {
72412
+ ...invalidPlan("auth scheme not yet drivable (e.g. HMAC / mTLS / provider-replay) \u2014 needs manual configuration")
72413
+ };
72414
+ case AUTH_TAG_NONE:
72415
+ return explicitPlan(AUTH_TAG_NONE, "none", "none", { cookie: false, standingMode: "none", requiredEnvKeys: [] });
72416
+ case AUTH_TAG_STANDING:
72417
+ return explicitPlan(AUTH_TAG_STANDING, "standing-secret", "standing-header", { cookie: false, standingMode: "required", requiredEnvKeys: [API_AUTH_HEADERS_ENV_KEY] });
72418
+ case AUTH_TAG_LOGIN_BEARER:
72419
+ return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "none", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
72420
+ case AUTH_TAG_LOGIN_COOKIE:
72421
+ return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
72422
+ case AUTH_TAG_COOKIE_STATE:
72423
+ return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
72424
+ }
72425
+ }
72426
+ const cookie = legacyNeedsCookieState(t, code, sharedFlag);
72427
+ const authedLegacy = t.includes("@auth") || t.includes("@needs-auth") || isApi;
72428
+ const selfLoginWithoutStoredCreds = t.includes("@auth") && !t.includes("@needs-auth") && !isApi && !!code?.trim() && !/TEST_USER_EMAIL|TEST_USER_PASSWORD|__vqaLogin\b/.test(code);
72429
+ const apiWithoutStoredCreds = isApi && !t.includes("@auth") && !t.includes("@needs-auth") && !!code?.trim() && !codeReadsStoredCredentials(code);
72430
+ const needsLoginCreds = authedLegacy && !selfLoginWithoutStoredCreds && !apiWithoutStoredCreds;
72431
+ return {
72432
+ version: 1,
72433
+ source: "legacy",
72434
+ acquisition: cookie ? "browser-bootstrap" : authedLegacy ? "request-login" : "none",
72435
+ presentation: authedLegacy ? "cookie" : "none",
72436
+ needsCookieState: cookie,
72437
+ needsStandingHeaders: false,
72438
+ // A legacy bare-@api test keeps injecting standing headers IF the project configured them
72439
+ // (preserving today's behavior) — but does not require them. Non-@api legacy tests never inject.
72440
+ standingHeadersMode: isApi ? "legacy-if-configured" : "none",
72441
+ needsLoginCreds,
72442
+ requiredEnvKeys: needsLoginCreds ? [...TEST_USER_ENV_KEYS] : []
72443
+ };
72444
+ }
72445
+ function authTypeTag(tags) {
72446
+ const t = Array.isArray(tags) ? tags : [];
72447
+ const explicit = explicitAuthTypeTags(t);
72448
+ return explicit.length === 1 ? explicit[0] : void 0;
72449
+ }
72450
+ 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;
72451
+ var init_auth_plan = __esm({
72452
+ "../shared/dist/auth-plan.js"() {
72453
+ "use strict";
72454
+ AUTH_TAG_COOKIE_STATE = "@auth-cookie-state";
72455
+ AUTH_TAG_LOGIN_COOKIE = "@auth-login-cookie";
72456
+ AUTH_TAG_LOGIN_BEARER = "@auth-login-bearer";
72457
+ AUTH_TAG_STANDING = "@auth-standing";
72458
+ AUTH_TAG_NONE = "@auth-none";
72459
+ AUTH_TAG_UNSUPPORTED = "@auth-unsupported";
72460
+ AUTH_TYPE_TAGS = [
72461
+ AUTH_TAG_COOKIE_STATE,
72462
+ AUTH_TAG_LOGIN_COOKIE,
72463
+ AUTH_TAG_LOGIN_BEARER,
72464
+ AUTH_TAG_STANDING,
72465
+ AUTH_TAG_NONE,
72466
+ AUTH_TAG_UNSUPPORTED
72467
+ ];
72468
+ AUTH_TAG_ALIASES = {
72469
+ "@auth-cookie": AUTH_TAG_COOKIE_STATE,
72470
+ "@auth-bearer": AUTH_TAG_LOGIN_BEARER,
72471
+ "@auth-token": AUTH_TAG_LOGIN_BEARER,
72472
+ "@auth-api-key": AUTH_TAG_STANDING,
72473
+ "@auth-standing-header": AUTH_TAG_STANDING
72474
+ };
72475
+ TEST_USER_ENV_KEYS = ["TEST_USER_EMAIL", "TEST_USER_PASSWORD"];
72476
+ API_AUTH_HEADERS_ENV_KEY = "API_AUTH_HEADERS";
72477
+ }
72478
+ });
72479
+
72480
+ // ../shared/dist/assertion-strength.js
72481
+ function parseLastActionProofMode(raw) {
72482
+ const v = (raw ?? "").trim().toLowerCase();
72483
+ return v === "on" || v === "shadow" ? v : "off";
72484
+ }
72485
+ function isUrlProof(stmt) {
72486
+ if (URL_PROOF.test(stmt))
72487
+ return true;
72488
+ let calls = 0;
72489
+ for (const open of stmt.matchAll(POLL_OPEN)) {
72490
+ if (++calls > POLL_CALLS_MAX)
72491
+ break;
72492
+ const from = open.index + open[0].length;
72493
+ const args = balancedArguments(stmt, from, MATCHER_ARGS_MAX);
72494
+ if (args === null)
72495
+ continue;
72496
+ const subject = pollSubject(args);
72497
+ if (!POLL_PAGE_URL.test(subject))
72498
+ continue;
72499
+ if (!POLL_PAGE_URL_ONLY.test(subject) || PAGE_VALUE_MATCHER.test(stmt.slice(from + args.length)))
72500
+ return true;
72501
+ }
72502
+ return false;
72503
+ }
72504
+ function liveExpectStatements(uncommented) {
72505
+ const masked = maskedOutsideStrings(uncommented).split("\n");
72506
+ return accumulateStatements(uncommented.split("\n")).filter((s) => EXPECT_STMT.test(s.stmt) && EXPECT_STMT.test(masked[s.startLine - 1] ?? ""));
72507
+ }
72508
+ function stripLineComment2(ln) {
72509
+ let quote = null;
72510
+ for (let i = 0; i < ln.length; i++) {
72511
+ const c = ln[i];
72512
+ if (quote) {
72513
+ if (c === quote && ln[i - 1] !== "\\")
72514
+ quote = null;
72515
+ continue;
72516
+ }
72517
+ if (c === '"' || c === "'" || c === "`") {
72518
+ quote = c;
72519
+ continue;
72520
+ }
72521
+ if (c === "/" && ln[i + 1] === "/")
72522
+ return ln.slice(0, i);
72523
+ }
72524
+ return ln;
72525
+ }
72526
+ function splitInteractionStatementsWithLines(lines) {
72527
+ const out = [];
72528
+ let depth = 0;
72529
+ let scaffold = false;
72530
+ for (let i = 0; i < lines.length; i++) {
72531
+ const ln = stripLineComment2(lines[i]).trim();
72532
+ if (!ln)
72533
+ continue;
72534
+ const isContinuation = out.length > 0 && (ln.startsWith(".") || depth > 0 && !scaffold);
72535
+ if (isContinuation) {
72536
+ out[out.length - 1].stmt += " " + ln;
72537
+ depth += parenDepthDelta(ln);
72538
+ } else {
72539
+ out.push({ stmt: ln, startLine: i + 1 });
72540
+ depth = parenDepthDelta(ln);
72541
+ scaffold = BLOCK_SCAFFOLD2.test(ln);
72542
+ }
72543
+ if (depth < 0)
72544
+ depth = 0;
72545
+ }
72546
+ return out;
72547
+ }
72548
+ function testBlockIds(lines) {
72549
+ const ids = new Array(lines.length).fill(0);
72550
+ let depth = 0;
72551
+ let open = false;
72552
+ let any = false;
72553
+ let block = 0;
72466
72554
  for (let i = 0; i < lines.length; i++) {
72467
- const ln = stripLineComment(lines[i]).trim();
72555
+ const ln = stripLineComment2(lines[i]).trim();
72468
72556
  if (!open) {
72469
72557
  if (!TEST_BLOCK_OPEN.test(ln))
72470
72558
  continue;
@@ -72548,7 +72636,7 @@ function expectSubjectSpan(stmt) {
72548
72636
  }
72549
72637
  return null;
72550
72638
  }
72551
- function resolveSubjectBindings(stmts, bindings, blockOfLine) {
72639
+ function resolveSubjectBindings(stmts, bindings, blockOfLine, boundAt) {
72552
72640
  if (bindings.length === 0)
72553
72641
  return [...stmts];
72554
72642
  return stmts.map((item) => {
@@ -72571,14 +72659,22 @@ function resolveSubjectBindings(stmts, bindings, blockOfLine) {
72571
72659
  subject = `${lead[1]}${found.expr.replace(/;\s*$/, "")}${subject.slice(lead[0].length)}`;
72572
72660
  line = found.line;
72573
72661
  }
72574
- return { ...item, stmt: `${item.stmt.slice(0, span.from)}${subject}${item.stmt.slice(span.to)}` };
72662
+ const out = { ...item, stmt: `${item.stmt.slice(0, span.from)}${subject}${item.stmt.slice(span.to)}` };
72663
+ if (boundAt !== void 0 && line !== item.startLine)
72664
+ boundAt.set(out, line);
72665
+ return out;
72575
72666
  });
72576
72667
  }
72577
72668
  function stripLoginScaffolding(code) {
72578
- const out = [];
72669
+ const lines = code.split("\n");
72670
+ return keptScaffoldingLines(lines).map((i) => lines[i]).join("\n");
72671
+ }
72672
+ function keptScaffoldingLines(lines) {
72673
+ const kept = [];
72579
72674
  let inAuth = false;
72580
72675
  let inInbox = false;
72581
- for (const ln of code.split("\n")) {
72676
+ for (let i = 0; i < lines.length; i++) {
72677
+ const ln = lines[i];
72582
72678
  if (!inAuth && !inInbox && AUTH_START.test(ln)) {
72583
72679
  inAuth = true;
72584
72680
  continue;
@@ -72599,9 +72695,9 @@ function stripLoginScaffolding(code) {
72599
72695
  }
72600
72696
  if (LOGIN_CALL_LINE.test(ln))
72601
72697
  continue;
72602
- out.push(ln);
72698
+ kept.push(i);
72603
72699
  }
72604
- return out.join("\n");
72700
+ return kept;
72605
72701
  }
72606
72702
  function stripCommentsAndLogs(code) {
72607
72703
  return stripCoverageComments(code).split("\n").filter((ln) => !/^\s*console\./.test(ln)).join("\n");
@@ -72753,6 +72849,41 @@ function isVacuousErrorAbsence(stmt, presence) {
72753
72849
  return false;
72754
72850
  return !presence().includes(m[1]);
72755
72851
  }
72852
+ function isVacuousAccountAbsence(stmt, shown, account) {
72853
+ if (!claimsAbsence(stmt))
72854
+ return false;
72855
+ if (!ABSENCE_MATCHER.test(stmt)) {
72856
+ const negated = NEGATED_TEXT.exec(stmt);
72857
+ if (!negated)
72858
+ return false;
72859
+ const read = account.exec(stmt.slice(negated.index));
72860
+ return read !== null && !shown().has(read[0]);
72861
+ }
72862
+ const m = account.exec(stmt);
72863
+ if (!m || shown().has(m[0]))
72864
+ return false;
72865
+ for (let at = endOfEnclosingCall(stmt, m.index + m[0].length); at < stmt.length; at = endOfEnclosingCall(stmt, at)) {
72866
+ SCOPING_STEP.lastIndex = at;
72867
+ if (SCOPING_STEP.test(stmt))
72868
+ return false;
72869
+ }
72870
+ return true;
72871
+ }
72872
+ function endOfEnclosingCall(stmt, from) {
72873
+ let depth = 0;
72874
+ for (let i = from; i < stmt.length; i++) {
72875
+ const c = stmt[i];
72876
+ if (c === "'" || c === '"') {
72877
+ for (i++; i < stmt.length && stmt[i] !== c; i++)
72878
+ if (stmt[i] === "\\")
72879
+ i++;
72880
+ } else if (c === "(")
72881
+ depth++;
72882
+ else if (c === ")" && depth-- === 0)
72883
+ return i + 1;
72884
+ }
72885
+ return stmt.length;
72886
+ }
72756
72887
  function isBareContainerVisibility(stmt) {
72757
72888
  return BARE_CONTAINER_ROLE.test(stmt) && !GET_BY_TEXT.test(stmt) && !HAS_TEXT_FILTER.test(stmt) && !TEXT_CONTENT_MATCHER.test(stmt);
72758
72889
  }
@@ -72834,7 +72965,7 @@ function roleNames(s) {
72834
72965
  function interactedControlNames(lines) {
72835
72966
  const names = /* @__PURE__ */ new Set();
72836
72967
  for (const raw of lines) {
72837
- const ln = stripLineComment(raw);
72968
+ const ln = stripLineComment2(raw);
72838
72969
  if (!INTERACT_VERB.test(ln))
72839
72970
  continue;
72840
72971
  for (const n of roleNames(ln))
@@ -73030,6 +73161,14 @@ function balancedArguments(code, from, limit = code.length) {
73030
73161
  }
73031
73162
  return null;
73032
73163
  }
73164
+ function expectCallTail(stmt) {
73165
+ const at = stmt.search(EXPECT_CALL_RE);
73166
+ if (at < 0)
73167
+ return null;
73168
+ const from = stmt.indexOf("(", at) + 1;
73169
+ const args = balancedArguments(stmt, from, MATCHER_ARGS_MAX);
73170
+ return args === null ? null : stmt.slice(from + args.length).trimStart();
73171
+ }
73033
73172
  function matcherArgumentsAreConstant(body, from) {
73034
73173
  MATCHER_OPEN.lastIndex = from;
73035
73174
  const opened = MATCHER_OPEN.exec(body);
@@ -73071,9 +73210,12 @@ function envValueKey(name) {
73071
73210
  function enteredValues(stmts) {
73072
73211
  const values = /* @__PURE__ */ new Map();
73073
73212
  const navLines = [];
73213
+ const accountConsts = /* @__PURE__ */ new Set();
73074
73214
  for (const { stmt, startLine } of stmts) {
73075
73215
  if (PAGE_NAV.test(stmt))
73076
73216
  navLines.push(startLine);
73217
+ for (const c of stmt.matchAll(ACCOUNT_CONST))
73218
+ accountConsts.add(c[1].replace(/\$/g, "\\$"));
73077
73219
  let m;
73078
73220
  ENTERED_VALUE.lastIndex = 0;
73079
73221
  const add = (value) => {
@@ -73089,7 +73231,8 @@ function enteredValues(stmts) {
73089
73231
  while (m = ENTERED_ENV.exec(stmt))
73090
73232
  add(envValueKey(m[1] ?? m[2] ?? m[3] ?? m[4]));
73091
73233
  }
73092
- return { values, navLines };
73234
+ const consts = accountConsts.size > 0 ? `|(?<![\\w$.])(?:${[...accountConsts].join("|")})(?![\\w$])` : "";
73235
+ return { values, navLines, account: new RegExp(`${ACCOUNT_ENV}${consts}`) };
73093
73236
  }
73094
73237
  function countBelow(lines, line) {
73095
73238
  let lo = 0;
@@ -73226,6 +73369,9 @@ function comparesWithItself(subject, tail) {
73226
73369
  return expected === squash2(m[1] === "toHaveCount" ? `${subject}.count()` : subject);
73227
73370
  }
73228
73371
  function asPlainAssertion(s) {
73372
+ return plainAssertion(s, false);
73373
+ }
73374
+ function plainAssertion(s, keepSelfCompare) {
73229
73375
  const { stmt } = s;
73230
73376
  if (COUNT_AT_LEAST_ZERO.test(stmt))
73231
73377
  return [];
@@ -73234,13 +73380,13 @@ function asPlainAssertion(s) {
73234
73380
  return [s];
73235
73381
  const plain = subject.replace(/^await\s+/, "").trim();
73236
73382
  const subjectAt = stmt.indexOf(subject, stmt.search(EXPECT_CALL_RE));
73237
- const tail = stmt.slice(subjectAt + subject.length).trimStart();
73383
+ const tail = (stmt.startsWith("expect.poll", stmt.search(EXPECT_CALL_RE)) ? expectCallTail(stmt) : null) ?? stmt.slice(subjectAt + subject.length).trimStart();
73238
73384
  const as = (rewritten) => [{ ...s, stmt: rewritten }];
73239
73385
  if (LOCATOR_EXPR.test(plain) && !LOCATOR_VALUE_READ.test(plain) && ALWAYS_TRUE_ON_OBJECT.test(tail))
73240
73386
  return [];
73241
73387
  if (DEFINED_VALUE_READ.test(plain) && ALWAYS_DEFINED.test(tail))
73242
73388
  return [];
73243
- if (comparesWithItself(plain, tail))
73389
+ if (!keepSelfCompare && comparesWithItself(plain, tail))
73244
73390
  return [];
73245
73391
  const visible = /^(.+)\.\s*isVisible\s*\(\s*\)$/s.exec(plain);
73246
73392
  if (visible && IS_VISIBLE_TRUE.test(tail))
@@ -73251,6 +73397,9 @@ function asPlainAssertion(s) {
73251
73397
  }
73252
73398
  return [s];
73253
73399
  }
73400
+ function assertionStrengthHoistedReadsOn(value = typeof process !== "undefined" ? process.env?.ASSERTION_STRENGTH_HOISTED_READS : void 0) {
73401
+ return /^(1|true|on)$/i.test((value ?? "").trim());
73402
+ }
73254
73403
  function creditsOutcomeAlways(stmt, entered, startLine) {
73255
73404
  if (!OUTCOME_ALWAYS.some((re) => re.test(stmt)))
73256
73405
  return false;
@@ -73283,6 +73432,22 @@ function successNoticeQuote(stmt) {
73283
73432
  }
73284
73433
  return null;
73285
73434
  }
73435
+ function namedPagePath(arg) {
73436
+ const text = arg.trim();
73437
+ let path;
73438
+ const quoted = /^(['"`])([^'"`]*)\1$/.exec(text);
73439
+ const regex = quoted ? null : /^\/((?:\\.|[^/\\])+)\/[a-z]*$/.exec(text);
73440
+ if (quoted) {
73441
+ path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\/\*\*$/, "/");
73442
+ } else if (regex) {
73443
+ path = regex[1].replace(/^\^/, "").replace(/^\.\*/, "").replace(/\$$/, "").replace(/\\([/.-])/g, "$1");
73444
+ } else {
73445
+ return null;
73446
+ }
73447
+ if (!PLAIN_PAGE_PATH.test(path))
73448
+ return null;
73449
+ return path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
73450
+ }
73286
73451
  function provesStoredControlValue(stmt, startLine, persistence) {
73287
73452
  if (!persistence || startLine === void 0)
73288
73453
  return false;
@@ -73309,7 +73474,64 @@ function reopenedAfterSave(startLine, persistence, reopens = persistence.reloadL
73309
73474
  }
73310
73475
  return lo < reopens.length && reopens[lo] < startLine;
73311
73476
  }
73312
- function classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstMutLine, persistence) {
73477
+ function actsBetween(withLines, from, to) {
73478
+ let acts = actLinesOf.get(withLines);
73479
+ if (acts === void 0) {
73480
+ acts = withLines.filter((s) => ACTS.test(s.stmt)).map((s) => s.startLine).sort((a, b) => a - b);
73481
+ actLinesOf.set(withLines, acts);
73482
+ }
73483
+ let lo = 0;
73484
+ let hi = acts.length;
73485
+ while (lo < hi) {
73486
+ const mid = lo + hi >> 1;
73487
+ if (acts[mid] <= from)
73488
+ lo = mid + 1;
73489
+ else
73490
+ hi = mid;
73491
+ }
73492
+ return lo < acts.length && acts[lo] < to;
73493
+ }
73494
+ function signInProofWindow(stmts, withLines, lines, blockIds) {
73495
+ if (blockIds === null)
73496
+ return void 0;
73497
+ const testBodies2 = /* @__PURE__ */ new Set();
73498
+ for (let i = 0; i < lines.length; i++) {
73499
+ const id = blockIds[i];
73500
+ if (id > 0 && (i === 0 || blockIds[i - 1] !== id) && TEST_BODY_OPEN.test(stripLineComment2(lines[i]).trim()))
73501
+ testBodies2.add(id);
73502
+ }
73503
+ const blockOf = (i) => blockIds[withLines[i].startLine - 1];
73504
+ const passwordAt = stmts.findIndex((stmt, i) => testBodies2.has(blockOf(i)) && ENV_PASSWORD_ENTRY.test(stmt));
73505
+ if (passwordAt < 0)
73506
+ return void 0;
73507
+ const body = blockOf(passwordAt);
73508
+ let clickAt = passwordAt + 1;
73509
+ while (clickAt < stmts.length && FORM_INPUT.test(stmts[clickAt]) && !ANY_CLICK.test(stmts[clickAt]))
73510
+ clickAt++;
73511
+ if (clickAt >= stmts.length || blockOf(clickAt) !== body || !ANY_CLICK.test(stmts[clickAt]) || NAV_LOCATOR.test(stmts[clickAt]))
73512
+ return void 0;
73513
+ let end = clickAt + 1;
73514
+ for (; end < stmts.length && blockOf(end) === body; end++) {
73515
+ if (CLOSERS_ONLY.test(stmts[end]))
73516
+ continue;
73517
+ if (!ASSERT_OR_WAIT.test(stmts[end]) || ACTS.test(stmts[end]))
73518
+ return void 0;
73519
+ }
73520
+ return { after: withLines[clickAt].startLine, until: end < stmts.length ? withLines[end].startLine : Number.POSITIVE_INFINITY };
73521
+ }
73522
+ function isAccountControlCheck(stmt) {
73523
+ if (!VISIBILITY_MATCHER.test(stmt) || NEGATED_ASSERT.test(stmt) || OPTION_ABSENCE.test(stmt))
73524
+ return false;
73525
+ const role = ACCOUNT_CONTROL_ROLE.exec(stmt);
73526
+ if (!role)
73527
+ return false;
73528
+ const rest = stmt.slice(role.index + role[0].length, role.index + role[0].length + 300);
73529
+ const close = rest.indexOf("}");
73530
+ const options = close < 0 ? rest : rest.slice(0, close);
73531
+ const name = options.match(/(?:^|[\s,])name\s*:\s*/);
73532
+ return name !== null && ACCOUNT_EMAIL_ENV.test(options.slice(name.index + name[0].length));
73533
+ }
73534
+ function classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstMutLine, persistence, signedIn) {
73313
73535
  if (mutated && hasAttributeTransition(stmts))
73314
73536
  return { kind: "durable" };
73315
73537
  const notices = [];
@@ -73319,6 +73541,13 @@ function classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstM
73319
73541
  };
73320
73542
  let presenceText;
73321
73543
  const presence = () => presenceText ??= stmts.filter((o) => !ABSENCE_MATCHER.test(o.stmt) && !NEGATED_ASSERT.test(o.stmt)).map((o) => o.stmt).join("\n");
73544
+ let shownAccounts;
73545
+ const shown = () => {
73546
+ if (shownAccounts)
73547
+ return shownAccounts;
73548
+ const ref = new RegExp(entered.account.source, "g");
73549
+ return shownAccounts = new Set(stmts.filter((o) => !claimsAbsence(o.stmt)).flatMap((o) => Array.from(o.stmt.matchAll(ref), (m) => m[0])));
73550
+ };
73322
73551
  for (const { stmt, startLine } of stmts) {
73323
73552
  if (firstMutLine !== void 0 && startLine !== void 0 && startLine < firstMutLine)
73324
73553
  continue;
@@ -73326,6 +73555,11 @@ function classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstM
73326
73555
  continue;
73327
73556
  if (COUNT_ONE.test(stmt) && quotedAssertedStrings(stmt).some((t) => clicked.has(t.toLowerCase())))
73328
73557
  continue;
73558
+ if (isVacuousAccountAbsence(stmt, shown, entered.account))
73559
+ continue;
73560
+ if (signedIn && startLine !== void 0 && startLine > signedIn.after && startLine < signedIn.until && isAccountControlCheck(stmt)) {
73561
+ return { kind: "durable" };
73562
+ }
73329
73563
  if (creditsOutcomeAlways(stmt, entered, startLine))
73330
73564
  return { kind: "durable" };
73331
73565
  if (mutated && !NEGATED_ASSERT.test(stmt) && MESSAGE_SURFACE.test(stmt) && OUTCOME_TEXT.test(assertedText(stmt))) {
@@ -73407,6 +73641,53 @@ function noticeOnlyExemption(clicked, noticeText, interactionStmts) {
73407
73641
  function detectOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstMutLine) {
73408
73642
  return classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstMutLine).kind !== "none";
73409
73643
  }
73644
+ function clickLabel(stmt) {
73645
+ let label = null;
73646
+ for (const m of stmt.matchAll(CONTROL_LABEL))
73647
+ label = m[2] ?? m[3] ?? null;
73648
+ return label;
73649
+ }
73650
+ function teardownLines(lines) {
73651
+ const out = new Array(lines.length).fill(false);
73652
+ let open = false;
73653
+ let depth = 0;
73654
+ for (let i = 0; i < lines.length; i++) {
73655
+ const ln = stripLineComment2(lines[i]).trim();
73656
+ let from = 0;
73657
+ if (!open) {
73658
+ const m = TEARDOWN_OPEN.exec(ln);
73659
+ if (!m)
73660
+ continue;
73661
+ open = true;
73662
+ depth = 0;
73663
+ from = m.index;
73664
+ }
73665
+ out[i] = true;
73666
+ let closed = false;
73667
+ let quote = null;
73668
+ for (let k = from; k < ln.length; k++) {
73669
+ const ch = ln[k];
73670
+ if (quote !== null) {
73671
+ if (ch === "\\")
73672
+ k++;
73673
+ else if (ch === quote)
73674
+ quote = null;
73675
+ continue;
73676
+ }
73677
+ if (ch === "'" || ch === '"' || ch === "`")
73678
+ quote = ch;
73679
+ else if (ch === "{")
73680
+ depth++;
73681
+ else if (ch === "}") {
73682
+ depth--;
73683
+ closed = true;
73684
+ }
73685
+ }
73686
+ if (depth <= 0 && closed)
73687
+ open = false;
73688
+ }
73689
+ return out;
73690
+ }
73410
73691
  function validateAssertionStrength(code, opts = {}) {
73411
73692
  const body = stripLoginScaffolding(code);
73412
73693
  const lines = body.split("\n");
@@ -73450,7 +73731,13 @@ function validateAssertionStrength(code, opts = {}) {
73450
73731
  const interactionStmts = inlineLocatorBindings(interactionStmtsRaw);
73451
73732
  const blockIds = testBlockIds(lines);
73452
73733
  const blockOfLine = (line) => blockIds?.[line - 1] ?? 0;
73453
- const checks = resolveSubjectBindings(stmts, scopedLocatorBindings(interactionStmtsWithLines, blockOfLine), blockOfLine);
73734
+ const hoistedReads = assertionStrengthHoistedReadsOn();
73735
+ const boundAt = hoistedReads ? /* @__PURE__ */ new Map() : void 0;
73736
+ const resolved = resolveSubjectBindings(stmts, scopedLocatorBindings(interactionStmtsWithLines, blockOfLine), blockOfLine, boundAt);
73737
+ const checks = boundAt === void 0 ? resolved : resolved.flatMap((s) => {
73738
+ const from = boundAt.get(s);
73739
+ return plainAssertion(s, from !== void 0 && actsBetween(interactionStmtsWithLines, from, s.startLine));
73740
+ });
73454
73741
  const insideTest = linesInsideTestBlocks(lines);
73455
73742
  const inTestOrder = (i) => insideTest === null || insideTest[interactionStmtsWithLines[i].startLine - 1] === true;
73456
73743
  let mutated = opts.interactionPerformed === true;
@@ -73460,9 +73747,34 @@ function validateAssertionStrength(code, opts = {}) {
73460
73747
  let navClicks = 0;
73461
73748
  const persistence = { mutLines: [], reloadLines: [], reloadCalls: [] };
73462
73749
  const openedPages = /* @__PURE__ */ new Set();
73750
+ let checkedPage = null;
73751
+ const savedOnCheckedPage = /* @__PURE__ */ new Set();
73752
+ const saveAt = (i, at) => {
73753
+ lastMutIdx = i;
73754
+ if (!inTestOrder(i))
73755
+ return;
73756
+ persistence.mutLines.push(at);
73757
+ if (firstMutIdx < 0)
73758
+ firstMutIdx = i;
73759
+ if (checkedPage !== null)
73760
+ savedOnCheckedPage.add(checkedPage);
73761
+ };
73762
+ const lastActionProof = opts.lastActionProof ?? "off";
73763
+ const teardown = lastActionProof === "off" ? null : teardownLines(lines);
73764
+ const acts = { last: null, prev: null, count: 0 };
73765
+ const noteAct = (i, at, kind) => {
73766
+ if (teardown === null || teardown[at - 1] || !inTestOrder(i))
73767
+ return;
73768
+ if (kind !== "enter")
73769
+ acts.count++;
73770
+ acts.prev = acts.last;
73771
+ acts.last = { i, kind };
73772
+ };
73463
73773
  for (let i = 0; i < interactionStmts.length; i++) {
73464
73774
  const ln = interactionStmts[i];
73465
73775
  const at = interactionStmtsWithLines[i].startLine;
73776
+ if (teardown !== null && acts.last?.kind === "fill" && ENTER_PRESS.test(ln))
73777
+ noteAct(i, at, "enter");
73466
73778
  if (RELOAD.test(ln)) {
73467
73779
  persistence.reloadLines.push(at);
73468
73780
  persistence.reloadCalls.push(at);
@@ -73470,32 +73782,31 @@ function validateAssertionStrength(code, opts = {}) {
73470
73782
  const opened = ln.match(/\.goto\s*\(\s*([^,)]+)/);
73471
73783
  if (opened) {
73472
73784
  const page = opened[1].trim();
73473
- if (openedPages.has(page))
73785
+ const path = namedPagePath(page);
73786
+ if (openedPages.has(page) || path !== null && savedOnCheckedPage.has(path))
73474
73787
  persistence.reloadLines.push(at);
73475
- else
73476
- openedPages.add(page);
73477
- }
73788
+ openedPages.add(page);
73789
+ checkedPage = null;
73790
+ }
73791
+ if (/\bpage\s*\.\s*go(?:Back|Forward)\s*\(/.test(ln))
73792
+ checkedPage = null;
73793
+ const checked = URL_CHECK_ARG.exec(ln);
73794
+ if (checked)
73795
+ checkedPage = namedPagePath(checked[1] ?? checked[2]);
73478
73796
  if (FORM_INPUT.test(ln)) {
73479
73797
  mutated = true;
73480
- lastMutIdx = i;
73481
- if (inTestOrder(i)) {
73482
- persistence.mutLines.push(at);
73483
- if (firstMutIdx < 0)
73484
- firstMutIdx = i;
73485
- }
73798
+ saveAt(i, at);
73799
+ noteAct(i, at, "fill");
73486
73800
  continue;
73487
73801
  }
73488
73802
  if (ANY_CLICK.test(ln)) {
73489
73803
  if (NAV_LOCATOR.test(ln)) {
73490
73804
  navClicks++;
73805
+ checkedPage = null;
73491
73806
  } else {
73492
73807
  mutated = true;
73493
- lastMutIdx = i;
73494
- if (inTestOrder(i)) {
73495
- persistence.mutLines.push(at);
73496
- if (firstMutIdx < 0)
73497
- firstMutIdx = i;
73498
- }
73808
+ saveAt(i, at);
73809
+ noteAct(i, at, "click");
73499
73810
  }
73500
73811
  }
73501
73812
  }
@@ -73528,9 +73839,28 @@ function validateAssertionStrength(code, opts = {}) {
73528
73839
  };
73529
73840
  }
73530
73841
  const orderedPersistence = firstMutLine !== void 0 ? persistence : void 0;
73531
- const proof = classifyOutcomeProof(checks, mutated, reloaded, clickedControlNames(interactionStmts), entered, firstMutLine, orderedPersistence);
73842
+ const signedIn = signInProofWindow(interactionStmts, interactionStmtsWithLines, lines, blockIds);
73843
+ const proof = classifyOutcomeProof(checks, mutated, reloaded, clickedControlNames(interactionStmts), entered, firstMutLine, orderedPersistence, signedIn);
73532
73844
  if (proof.kind === "durable") {
73533
73845
  const signals2 = base2({ interacted, mutated, navOnly, hasOutcomeProof: true, terminal });
73846
+ const { last, prev } = acts;
73847
+ 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])))) {
73848
+ const lastLine = interactionStmtsWithLines[last.i].startLine;
73849
+ const clicked = clickedControlNames(interactionStmts);
73850
+ const after = classifyOutcomeProof(checks, mutated, reloaded, clicked, entered, lastLine, orderedPersistence, signedIn);
73851
+ const unproven = after.kind === "none" || after.kind === "notice" && noticeOnlyExemption(clicked, after.noticeText, interactionStmts) === null;
73852
+ if (unproven && lastActionProof === "on") {
73853
+ const line = (keptScaffoldingLines(code.split("\n"))[lastLine - 1] ?? lastLine - 1) + 1;
73854
+ return {
73855
+ ok: false,
73856
+ severity: "block",
73857
+ reason: `the last save (line ${line}) has no outcome check after it (the earlier check proves only the earlier action)`,
73858
+ signals: { ...signals2, lastActionUnproven: true }
73859
+ };
73860
+ }
73861
+ if (unproven)
73862
+ signals2.lastActionUnproven = true;
73863
+ }
73534
73864
  return { ok: true, severity: "none", reason: "proves an outcome (count / diff / value / revealed content)", signals: signals2 };
73535
73865
  }
73536
73866
  if (mutated && proof.kind === "notice") {
@@ -73579,7 +73909,7 @@ function validateAssertionStrength(code, opts = {}) {
73579
73909
  signals
73580
73910
  };
73581
73911
  }
73582
- const urlProofAfter = interactionStmts.some((s, i) => i > lastMutIdx && URL_PROOF.test(s));
73912
+ const urlProofAfter = interactionStmts.some((s, i) => i > lastMutIdx && isUrlProof(s));
73583
73913
  if (urlProofAfter) {
73584
73914
  return {
73585
73915
  ok: false,
@@ -73616,7 +73946,7 @@ function validateAssertionStrength(code, opts = {}) {
73616
73946
  signals
73617
73947
  };
73618
73948
  }
73619
- var ERROR_BANNER, OUTCOME_ALWAYS, FORM_INPUT, ANY_CLICK, NAV_LOCATOR, URL_PROOF, RELOAD, EXPECT_STMT, BLOCK_SCAFFOLD2, TEST_BLOCK_OPEN, LOCATOR_BINDING, EXPECT_OPEN, STATE_MATCHER, CHROME_LOCATOR, CONTENT_ROLE, GET_BY_TEXT, BARE_CONTAINER_ROLE, HAS_TEXT_FILTER, OUTCOME_TEXT, SUCCESS_NOTICE_TEXT, NOT_A_SUCCESS_NOTICE, ANNOUNCE_ROLE, MESSAGE_SURFACE, TEXT_CONTENT_MATCHER, VISIBILITY_MATCHER, ADDITIVE_AFFORDANCE, EXCLUDE_AFFORDANCE, EMPTY_STATE_TEXT, AUTH_START, AUTH_END, LOGIN_CALL_LINE, INBOX_START, INBOX_END, NEGATED_ASSERT, NEGATED_ASSERT_NOT, CONST_CHAIN_MAX_DEPTH, TYPED_INPUT, TITLE_LINE, FAILURE_STATUS_ASSERT, FAILED_LIST_ASSERT, ENDPOINT_DOWN, NAVIGATION_RESPONSE_VAR, DIRECT_REQUEST_VAR, CATCH_ALL_PATTERN, GUARD_HANDLER, ABSENCE_MATCHER, VALUE_ABSENCE, OPTION_ABSENCE, NOT_BEFORE_MATCHER, ERROR_TEXT_WORDS, TEXT_REGEX_ARG, NEGATED_TEXT, ASYNC_DESTRUCTURE_OPEN, REQUEST_WORD, ROLE_NAME_RE, INTERACT_VERB, ROLE_SIG_RE, TRANSIENT_CONTROL_ROLES, POLL_CALLBACK, CONSTANT_WORD, CONSTANT_GLUE_CHARS, WORD_CHAR, MATCHER_ARGS_MAX, MATCHER_OPEN, ENTERED_VALUE, PAGE_NAV, ENV_READ, ENTERED_ENV, HAVE_VALUE_ENV, IN_FLIGHT_TEXT, LOCATOR_VALUE_READ, LOCATOR_EXPR, ALWAYS_TRUE_ON_OBJECT, COUNT_AT_LEAST_ZERO, DEFINED_VALUE_READ, ALWAYS_DEFINED, SELF_COMPARE_MATCHER, IS_VISIBLE_TRUE, PAGE_VALUE_MATCHER, COUNT_ONE, FORM_CONTROL_LOCATOR, CONTROL_VALUE_MATCHER, COPY_CONTROL, COPIED_NOTICE, SEND_CONTROL, SEND_NOTICE, MARK_READ_CONTROL, APP_DATA_FIELD;
73949
+ 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;
73620
73950
  var init_assertion_strength = __esm({
73621
73951
  "../shared/dist/assertion-strength.js"() {
73622
73952
  "use strict";
@@ -73625,6 +73955,7 @@ var init_assertion_strength = __esm({
73625
73955
  init_assertion_proof();
73626
73956
  init_auth_handoff_coverage();
73627
73957
  init_expect_call();
73958
+ init_auth_plan();
73628
73959
  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;
73629
73960
  OUTCOME_ALWAYS = [
73630
73961
  /\.toHaveCount\s*\(/,
@@ -73641,7 +73972,7 @@ var init_assertion_strength = __esm({
73641
73972
  // form field value
73642
73973
  /\.toMatch\s*\(/,
73643
73974
  // string/regex content match (e.g. textContent() vs /[2-9] selected/)
73644
- /(?<!\.not\s*)\.\s*toBeDisabled\s*\(/,
73975
+ /\.(?<!\.not\s*\.)\s*toBeDisabled\s*\(/,
73645
73976
  // a control became DISABLED — a gating/validation outcome (not.toBeDisabled is the toBeEnabled twin)
73646
73977
  /\.not\.toBeVisible\s*\(/,
73647
73978
  /\.toBeHidden\s*\(/
@@ -73650,12 +73981,16 @@ var init_assertion_strength = __esm({
73650
73981
  FORM_INPUT = /\.(fill|selectOption|setInputFiles|check|uncheck|setChecked|pressSequentially)\s*\(|keyboard\.type\s*\(/;
73651
73982
  ANY_CLICK = /\.click\s*\(/;
73652
73983
  NAV_LOCATOR = /getByRole\s*\(\s*['"`](?:link|menuitem)['"`]/i;
73653
- URL_PROOF = /\.toHaveURL\s*\(|\.waitForURL\s*\(|\bexpect\s*\(\s*(?:await\s+)?page\s*\.\s*url\s*\(\s*\)\s*\)|\bexpect\s*\.\s*poll\s*\(\s*(?:async\s*)?\(\s*\)\s*=>\s*(?:\{\s*return\s+)?page\s*\.\s*url\s*\(\s*\)/;
73984
+ 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*\(/;
73985
+ POLL_OPEN = /\bexpect\s*\.\s*poll\s*\(/g;
73986
+ POLL_PAGE_URL = /^page\s*\.\s*url\s*\(\s*\)/;
73987
+ POLL_PAGE_URL_ONLY = /^page\s*\.\s*url\s*\(\s*\)$/;
73988
+ POLL_CALLS_MAX = 8;
73654
73989
  RELOAD = /\.reload\s*\(/;
73655
73990
  EXPECT_STMT = /\bexpect\s*[.(]/;
73656
73991
  BLOCK_SCAFFOLD2 = /=>|\b(?:test|describe|it|beforeEach|afterEach|beforeAll|afterAll|function)\b/;
73657
73992
  TEST_BLOCK_OPEN = /^(?:test|it)(?:\.(?:only|skip|fixme|slow|serial|describe\.serial))?\s*\(|^(?:test\.)?(?:beforeEach|beforeAll)\s*\(/;
73658
- LOCATOR_BINDING = /^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(.+?(?:getByRole|getByText|getByLabel|getByPlaceholder|getByTestId|getByAltText|getByTitle|locator)\s*\(.+)$/;
73993
+ 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*\(.+)$/;
73659
73994
  EXPECT_OPEN = /\bexpect(?:\.soft)?\s*\(/;
73660
73995
  STATE_MATCHER = /\.(toBeChecked|toBeVisible|toHaveValue|toBeDisabled|toBeEnabled)\s*\(/;
73661
73996
  CHROME_LOCATOR = /getByRole\s*\(\s*['"`](?:heading|navigation|banner|main|contentinfo|complementary|region|search|tablist|tab|button|link|menuitem|textbox|searchbox|combobox|spinbutton)['"`]/i;
@@ -73675,7 +74010,7 @@ var init_assertion_strength = __esm({
73675
74010
  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;
73676
74011
  AUTH_START = /^\s*\/\/\s*>>>\s*VALIDATE_QA_AUTH/;
73677
74012
  AUTH_END = /^\s*\/\/\s*<<<\s*VALIDATE_QA_AUTH/;
73678
- LOGIN_CALL_LINE = /^\s*await\s+__vqaLogin\s*\(\s*page\s*\)\s*;?\s*$/;
74013
+ LOGIN_CALL_LINE = /^\s*await\s+__vqaLogin\s*\(\s*page\s*\)\s*(?:;\s*)?$/;
73679
74014
  INBOX_START = /^\s*\/\/\s*>>>\s*VALIDATE_QA_INBOX/;
73680
74015
  INBOX_END = /^\s*\/\/\s*<<<\s*VALIDATE_QA_INBOX/;
73681
74016
  NEGATED_ASSERT = /\.not\s*\.\s*to[A-Z]\w*|\.toBeHidden\s*\(/;
@@ -73685,7 +74020,7 @@ var init_assertion_strength = __esm({
73685
74020
  TITLE_LINE = /^\s*(?:test|describe)(?:\.\w+)?\s*\(/;
73686
74021
  FAILURE_STATUS_ASSERT = [
73687
74022
  /\.status\s*\(\s*\)[^;]{0,200}\.(?:toBeGreaterThanOrEqual|toBeGreaterThan)\s*\(\s*(?:399|[45]\d\d)\b/,
73688
- /\bexpect\s*\(\s*[^;]{0,200}\bstatus\s*(?:\(\s*\))?\s*>=?\s*(?:399|[45]\d\d)\b[^;]{0,200}\)\s*\.\s*(?:toBe\s*\(\s*true|toBeTruthy)/,
74023
+ /\bexpect\s*\(\s*[^;]{0,200}\bstatus\s*(?:\(\s*\)\s*)?>=?\s*(?:399|[45]\d\d)\b[^;]{0,200}\)\s*\.\s*(?:toBe\s*\(\s*true|toBeTruthy)/,
73689
74024
  /\bexpect\s*\([^;]{0,200}\.ok\s*\(\s*\)\s*\)\s*\.\s*(?:toBe\s*\(\s*false\s*\)|toBeFalsy\s*\()/
73690
74025
  ];
73691
74026
  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;
@@ -73700,6 +74035,7 @@ var init_assertion_strength = __esm({
73700
74035
  NOT_BEFORE_MATCHER = /\.not\s*\.\s*(?=to[A-Z])/g;
73701
74036
  ERROR_TEXT_WORDS = /error|fail|wrong|try again|unable|could ?n.?t|can.?t|problem|invalid|unavailable/i;
73702
74037
  TEXT_REGEX_ARG = /(?:getByText|hasText|getByRole\([^)]{0,200}name)\s*[:(]\s*(\/(?:\\.|[^/\\\n])+\/[a-z]*)/;
74038
+ SCOPING_STEP = /(?:\s*\.\s*(?:first|last|nth)\s*\([^()]*\))*\s*\.\s*(?:getBy[A-Z]\w*|locator|filter)\s*\(/y;
73703
74039
  NEGATED_TEXT = /\.not\s*\.\s*(?:toHaveText|toContainText)\s*\(\s*(?:['"`]([^'"`]*)['"`]|\/((?:\\.|[^/\\\n])+)\/)?/;
73704
74040
  ASYNC_DESTRUCTURE_OPEN = /async\s*\(\s*\{/g;
73705
74041
  REQUEST_WORD = /\brequest\b/;
@@ -73726,6 +74062,9 @@ var init_assertion_strength = __esm({
73726
74062
  ENV_READ = String.raw`process\.env(?:(?:\?\.|\.)([A-Za-z_]\w*)|(?:\?\.)?\[\s*['"]([A-Za-z_]\w*)['"]\s*\])`;
73727
74063
  ENTERED_ENV = new RegExp(String.raw`\.(?:fill|pressSequentially)\s*\(\s*${ENV_READ}|keyboard\.type\s*\(\s*${ENV_READ}`, "g");
73728
74064
  HAVE_VALUE_ENV = new RegExp(String.raw`\.toHaveValue\s*\(\s*${ENV_READ}`);
74065
+ ACCOUNT_KEYS = TEST_USER_ENV_KEYS.join("|");
74066
+ ACCOUNT_ENV = `process\\.env(?:\\??\\.(?:${ACCOUNT_KEYS})\\b|(?:\\?\\.)?\\[\\s*['"\`](?:${ACCOUNT_KEYS})['"\`]\\s*\\])`;
74067
+ ACCOUNT_CONST = new RegExp(`\\b(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)\\s*(?::\\s*string\\s*)?=\\s*${ACCOUNT_ENV}`, "g");
73729
74068
  IN_FLIGHT_TEXT = /^(?:(?:sending|saving|loading|searching|checking|processing|submitting|preparing|updating|please wait|working|one moment)\b[^.]{0,40}\.{2,3}|\.{3}|…)$/i;
73730
74069
  LOCATOR_VALUE_READ = /\.(?:count|textContent|innerText|innerHTML|inputValue|getAttribute|isVisible|isHidden|isChecked|isEnabled|isDisabled|isEditable|allTextContents|allInnerTexts|evaluate|evaluateAll|boundingBox|all)\s*\(/;
73731
74070
  LOCATOR_EXPR = /^(?:this\.)?page\s*\.\s*(?:getBy\w+|locator)\s*\(/;
@@ -73737,20 +74076,47 @@ var init_assertion_strength = __esm({
73737
74076
  IS_VISIBLE_TRUE = /^\)*\s*\.\s*(?:toBe\s*\(\s*true\s*\)|toBeTruthy\s*\(\s*\))/;
73738
74077
  PAGE_VALUE_MATCHER = /^\)\s*\.\s*(?:toBe|toEqual|toStrictEqual|toMatch|toContain)\s*\(/;
73739
74078
  COUNT_ONE = /\.\s*toHaveCount\s*\(\s*1\s*\)/;
74079
+ URL_CHECK_ARG = /\bpage\s*\.\s*waitForURL\s*\(\s*([^,)]+)|\bexpect(?:\s*\.\s*soft)?\s*\(\s*page\s*\)\s*\.\s*toHaveURL\s*\(\s*([^,)]+)/;
74080
+ PLAIN_PAGE_PATH = /^\/[\w\-./~%]*(?:[?#][^\s]*)?$/;
73740
74081
  FORM_CONTROL_LOCATOR = /getByRole\s*\(\s*['"`](?:combobox|switch|checkbox|radio|textbox|searchbox|spinbutton|slider)['"`]|getByLabel\s*\(|getByPlaceholder\s*\(/;
73741
74082
  CONTROL_VALUE_MATCHER = /\.(?:toHaveValue|toHaveText|toContainText)\s*\(|\.toHaveAttribute\s*\(\s*['"`]aria-checked['"`]/;
74083
+ ACCOUNT_EMAIL_ENV = /^process\.env(?:(?:\?\.|\.)TEST_USER_EMAIL\b|(?:\?\.)?\[\s*['"]TEST_USER_EMAIL['"]\s*\])/;
74084
+ ENV_PASSWORD_ENTRY = /\.(?:fill|pressSequentially)\s*\(\s*process\.env(?:(?:\?\.|\.)TEST_USER_PASSWORD\b|(?:\?\.)?\[\s*['"]TEST_USER_PASSWORD['"]\s*\])/;
74085
+ ACCOUNT_CONTROL_ROLE = /getByRole\s*\(\s*['"`](?:button|link|menuitem)['"`]\s*,\s*\{/;
74086
+ ASSERT_OR_WAIT = /^(?:await\s+)?(?:expect(?:\.soft|\.poll)?\s*\(|[\w$]+(?:\.[\w$]+)*\.waitFor[A-Za-z]*\s*\()/;
74087
+ 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*\(/;
74088
+ actLinesOf = /* @__PURE__ */ new WeakMap();
74089
+ CLOSERS_ONLY = /^[})\];,\s]+$/;
74090
+ TEST_BODY_OPEN = /^(?:test|it)(?:\.(?:only|skip|fixme|slow))?\s*\(/;
73742
74091
  COPY_CONTROL = /\bcopy\b/i;
73743
74092
  COPIED_NOTICE = /\bcopied\b/i;
73744
74093
  SEND_CONTROL = /\b(send|submit|subscribe|sign up for|contact|feedback|join (the )?waitlist)\b/i;
73745
74094
  SEND_NOTICE = /\b(thank|thanks|sent|subscribed|received|in touch)\b/i;
73746
74095
  MARK_READ_CONTROL = /\bmark(?:ed)? as read\b|\bmark all(?: as)? read\b/i;
73747
74096
  APP_DATA_FIELD = /\b(title|name of|amount|price|quantity|sku|status|due|assignee|project|ticket|invoice|order)\b/i;
74097
+ COMMIT_CONTROL = /\b(?:save|submit|update|apply|add|create|post|send|publish)\b|\bconfirm\s+changes\b/i;
74098
+ 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;
74099
+ DESTRUCTIVE_CONTROL = /\b(?:delete|remove|discard)\b/i;
74100
+ ENTER_PRESS = /\.press\s*\(\s*['"`]Enter['"`]/;
74101
+ CONTROL_LABEL = /(?:name\s*:\s*|getBy(?:Text|Label|Title|TestId)\s*\(\s*|has-text\(\s*)(?:(['"`])((?:(?!\1).)*)\1|\/((?:\\.|[^/\\])+)\/)/g;
74102
+ isCommitClick = (stmt) => {
74103
+ const label = clickLabel(stmt);
74104
+ return label !== null && COMMIT_CONTROL.test(label) && !NOT_COMMIT_CONTROL.test(label) && !EXCLUDE_AFFORDANCE.test(label);
74105
+ };
74106
+ isDestructiveClick = (stmt) => {
74107
+ const label = clickLabel(stmt);
74108
+ return label !== null && DESTRUCTIVE_CONTROL.test(label);
74109
+ };
74110
+ TEARDOWN_OPEN = /\bfinally\s*\{|^(?:test\.)?(?:after|before)(?:Each|All)\s*\(/;
73748
74111
  }
73749
74112
  });
73750
74113
 
73751
74114
  // ../shared/dist/run-classification.js
73752
- function isUserCancelledError(error2) {
73753
- return typeof error2 === "string" && error2.trim().startsWith(USER_CANCELLED_RUN_ERROR);
74115
+ function isCancelledReason(error2) {
74116
+ if (typeof error2 !== "string")
74117
+ return false;
74118
+ const text = error2.trim();
74119
+ return CANCELLED_REASON_PREFIXES.some((prefix) => text.startsWith(prefix));
73754
74120
  }
73755
74121
  function isHeldHealRun(run) {
73756
74122
  return run?.status === "PASSED" && typeof run.error === "string" && run.error.startsWith(HEAL_PATCH_HELD_RUN_ERROR_PREFIX);
@@ -73783,7 +74149,7 @@ function pickLatestHealthRun(runs) {
73783
74149
  for (const run of runs) {
73784
74150
  if (run.mode === "auth-setup")
73785
74151
  continue;
73786
- if (run.status === "ERROR" && isUserCancelledError(run.error))
74152
+ if (run.status === "ERROR" && isCancelledReason(run.error))
73787
74153
  continue;
73788
74154
  if (isHeldHealRun(run))
73789
74155
  continue;
@@ -73801,6 +74167,15 @@ function pickDisplayRun(runs) {
73801
74167
  return latest;
73802
74168
  return pickLatestHealthRun(runs) ?? latest;
73803
74169
  }
74170
+ function featureRowRunStatus(runs) {
74171
+ const latest = runs.find((run) => run.mode !== "auth-setup");
74172
+ if (!latest)
74173
+ return null;
74174
+ const decisive = pickLatestHealthRun(runs.filter((run) => !isEnvironmentalError(run)));
74175
+ if (decisive)
74176
+ return decisive.status;
74177
+ return latest.status === "PENDING" || latest.status === "RUNNING" ? latest.status : null;
74178
+ }
73804
74179
  function isEnvironmentalError(run) {
73805
74180
  if (!run)
73806
74181
  return false;
@@ -73832,7 +74207,7 @@ function classifyErrorMessage(error2) {
73832
74207
  return "config_issue";
73833
74208
  if (EMAIL_NOT_RECEIVED_PREFIXES.some((prefix) => normalized.startsWith(prefix)))
73834
74209
  return "email_not_received";
73835
- if (isUserCancelledError(error2))
74210
+ if (isCancelledReason(error2))
73836
74211
  return "cancelled";
73837
74212
  if (isRetriedRunInterruption(error2))
73838
74213
  return "interrupted";
@@ -73867,7 +74242,7 @@ function heldHealRunWhere() {
73867
74242
  function excludeCancelledRunsWhere() {
73868
74243
  return {
73869
74244
  NOT: [
73870
- { status: "ERROR", error: { not: null, startsWith: USER_CANCELLED_RUN_ERROR } },
74245
+ { status: "ERROR", OR: CANCELLED_REASON_PREFIXES.map((prefix) => ({ error: { not: null, startsWith: prefix } })) },
73871
74246
  heldHealRunWhere()
73872
74247
  ]
73873
74248
  };
@@ -73945,11 +74320,25 @@ function reportedBugWhere() {
73945
74320
  function bugReviewStatus(status) {
73946
74321
  return status === "QUARANTINED" ? "confirmed" : "needs-review";
73947
74322
  }
73948
- var USER_CANCELLED_RUN_ERROR, HEAL_PATCH_HELD_RUN_ERROR_PREFIX, RUNNER_INTERRUPT_ERROR_PREFIX, SERVER_INTERRUPT_ERROR_PREFIX, RUN_LOST_ERROR_PREFIX, RUN_INTERRUPTED_ERROR_PREFIXES, RUN_INTERRUPTION_RETRIED_MARKER, RUN_INTERRUPTION_NOT_RETRIED_MARKERS, BUG_ASSERTION_TAG, UNEXPECTED_PASS_ERROR_PREFIX, BUG_TEST_BROKEN_ERROR_PREFIX, BUG_TEST_BROKEN_REASON, TEST_DID_NOT_RUN_ERROR, EMAIL_NOT_RECEIVED_PREFIXES, ENVIRONMENTAL_ERROR_PREFIXES, ENVIRONMENTAL_ERROR_STORED_PREFIXES, BUG_SURFACE_SUPPRESSING_TAGS, QUARANTINE_CLOSE_TAGS, BUG_CANDIDATE_TAG, BUG_TEST_BROKEN_TAG;
74323
+ 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;
73949
74324
  var init_run_classification = __esm({
73950
74325
  "../shared/dist/run-classification.js"() {
73951
74326
  "use strict";
73952
74327
  USER_CANCELLED_RUN_ERROR = "Cancelled by user";
74328
+ PLATFORM_SUPPORT_CANCELLED_RUN_ERROR = "Cancelled by platform support";
74329
+ PLATFORM_ADMIN_CANCELLED_RUN_ERROR = "Cancelled by platform admin";
74330
+ ADMIN_FORCE_CANCELLED_RUN_ERROR = "Admin force-cancelled all active runs";
74331
+ CHAIN_CANCELLED_RUN_ERROR = "Cancelled: parent run cancelled";
74332
+ RETRY_HELD_RUN_ERROR = "Cancelled: auto generation paused";
74333
+ CANCELLED_REASON_PREFIXES = [
74334
+ USER_CANCELLED_RUN_ERROR,
74335
+ PLATFORM_SUPPORT_CANCELLED_RUN_ERROR,
74336
+ PLATFORM_ADMIN_CANCELLED_RUN_ERROR,
74337
+ ADMIN_FORCE_CANCELLED_RUN_ERROR,
74338
+ CHAIN_CANCELLED_RUN_ERROR,
74339
+ RETRY_HELD_RUN_ERROR
74340
+ ];
74341
+ isUserCancelledError = isCancelledReason;
73953
74342
  HEAL_PATCH_HELD_RUN_ERROR_PREFIX = "Heal patch held";
73954
74343
  RUNNER_INTERRUPT_ERROR_PREFIX = "Runner shutting down";
73955
74344
  SERVER_INTERRUPT_ERROR_PREFIX = "Run interrupted by server restart";
@@ -73978,9 +74367,10 @@ var init_run_classification = __esm({
73978
74367
  ];
73979
74368
  ENVIRONMENTAL_ERROR_STORED_PREFIXES = [
73980
74369
  ...ENVIRONMENTAL_ERROR_PREFIXES.flatMap((prefix) => [prefix, `Error: ${prefix}`]),
73981
- // Written by the cancel routes as is, with no prefix wrapper.
73982
- USER_CANCELLED_RUN_ERROR
74370
+ // Written by the cancels as is, with no prefix wrapper.
74371
+ ...CANCELLED_REASON_PREFIXES
73983
74372
  ];
74373
+ FEATURE_ROW_RUN_WINDOW = 10;
73984
74374
  BUG_SURFACE_SUPPRESSING_TAGS = ["@rate-limit-blocked", "@quota-exceeded", "@auth-lost"];
73985
74375
  QUARANTINE_CLOSE_TAGS = [
73986
74376
  "@bug-found",
@@ -76300,290 +76690,116 @@ function redirectOnAppSite(appHosts, redirectHost) {
76300
76690
  if (onSharedPlatform(app) || onSharedPlatform(target))
76301
76691
  return target === app;
76302
76692
  if (endsWithLabel(target, app))
76303
- return true;
76304
- const site = siteOf(app);
76305
- return endsWithLabel(target, site) && siteOf(target) === site;
76306
- });
76307
- }
76308
- function onSharedPlatform(host) {
76309
- return isOnSharedPlatform(host);
76310
- }
76311
- function namesASite(host) {
76312
- return hostNamesASite(host);
76313
- }
76314
- function hostOf(url) {
76315
- if (!url)
76316
- return null;
76317
- try {
76318
- return new URL(url).hostname.toLowerCase();
76319
- } catch {
76320
- return null;
76321
- }
76322
- }
76323
- function projectAppHosts(baseUrls) {
76324
- const hosts = baseUrls.map(hostOf).filter((host) => !!host);
76325
- return [...new Set(hosts)];
76326
- }
76327
- var SHARED_AUTH_SUFFIXES;
76328
- var init_google_app_site = __esm({
76329
- "../shared/dist/google-app-site.js"() {
76330
- "use strict";
76331
- init_origin_domain();
76332
- SHARED_AUTH_SUFFIXES = [
76333
- "auth0.com",
76334
- "clerk.accounts.dev",
76335
- "supabase.co",
76336
- "firebaseapp.com",
76337
- "web.app",
76338
- "amazoncognito.com",
76339
- "stytch.com",
76340
- "kinde.com"
76341
- ];
76342
- }
76343
- });
76344
-
76345
- // ../shared/dist/google-broker-lines.js
76346
- function googleBrokerUnavailableLine(lines, testError) {
76347
- if (!TIMEOUT_ERROR.test(testError ?? ""))
76348
- return null;
76349
- let last = null;
76350
- for (const raw of lines ?? []) {
76351
- for (const line of String(raw).split("\n")) {
76352
- const text = line.trim();
76353
- if (GOOGLE_BROKER_UNAVAILABLE.test(text) || GOOGLE_BROKER_ASKED.test(text))
76354
- last = text;
76355
- else if (GOOGLE_SIGNED_IN.test(text) || text.startsWith("[google-sign-in] "))
76356
- last = null;
76357
- }
76358
- }
76359
- return last;
76360
- }
76361
- function stepConsoleLines(result) {
76362
- const steps = Array.isArray(result) ? result : result && typeof result === "object" && Array.isArray(result.stepResults) ? result.stepResults : [];
76363
- return steps.flatMap((step) => {
76364
- const logs = step && typeof step === "object" ? step.consoleLogs : null;
76365
- return Array.isArray(logs) ? logs.filter((line) => typeof line === "string") : [];
76366
- });
76367
- }
76368
- var GOOGLE_BROKER_UNAVAILABLE, GOOGLE_BROKER_ASKED, GOOGLE_SIGNED_IN, TIMEOUT_ERROR;
76369
- var init_google_broker_lines = __esm({
76370
- "../shared/dist/google-broker-lines.js"() {
76371
- "use strict";
76372
- GOOGLE_BROKER_UNAVAILABLE = /^\[google-sign-in\] (?:(?:busy|rate_limited|timeout|offline): |failed: Google sign-in service (?:answered 50[234]\b|unreachable))/;
76373
- GOOGLE_BROKER_ASKED = /^\[google-sign-in\] the app started Google sign-in; asking the broker/;
76374
- GOOGLE_SIGNED_IN = /^\[google-sign-in\] returned to the app with Google sign-in/;
76375
- TIMEOUT_ERROR = /Test timeout of \d+ms exceeded|TimeoutError|Timeout:? \d+ ?ms/i;
76376
- }
76377
- });
76378
-
76379
- // ../shared/dist/admin-view-return-to.js
76380
- function sanitizeAdminViewReturnTo(value) {
76381
- if (typeof value !== "string")
76382
- return ADMIN_VIEW_DEFAULT_RETURN_TO;
76383
- const trimmed = value.trim();
76384
- if (!trimmed || trimmed.length > MAX_RETURN_TO_LENGTH)
76385
- return ADMIN_VIEW_DEFAULT_RETURN_TO;
76386
- if (!trimmed.startsWith("/") || trimmed.startsWith("//"))
76387
- return ADMIN_VIEW_DEFAULT_RETURN_TO;
76388
- if (trimmed.includes("\\") || /[\s\u0000-\u001f\u007f]/.test(trimmed))
76389
- return ADMIN_VIEW_DEFAULT_RETURN_TO;
76390
- let url;
76391
- try {
76392
- url = new URL(trimmed, PLACEHOLDER_ORIGIN);
76393
- } catch {
76394
- return ADMIN_VIEW_DEFAULT_RETURN_TO;
76395
- }
76396
- if (url.origin !== PLACEHOLDER_ORIGIN)
76397
- return ADMIN_VIEW_DEFAULT_RETURN_TO;
76398
- if (BLOCKED_PATH_RE.test(url.pathname))
76399
- return ADMIN_VIEW_DEFAULT_RETURN_TO;
76400
- const out = `${url.pathname}${url.search}${url.hash}`;
76401
- if (out.startsWith("//") || out.includes("\\"))
76402
- return ADMIN_VIEW_DEFAULT_RETURN_TO;
76403
- return out;
76404
- }
76405
- var ADMIN_VIEW_DEFAULT_RETURN_TO, MAX_RETURN_TO_LENGTH, PLACEHOLDER_ORIGIN, BLOCKED_PATH_RE;
76406
- var init_admin_view_return_to = __esm({
76407
- "../shared/dist/admin-view-return-to.js"() {
76408
- "use strict";
76409
- ADMIN_VIEW_DEFAULT_RETURN_TO = "/sessions";
76410
- MAX_RETURN_TO_LENGTH = 2048;
76411
- PLACEHOLDER_ORIGIN = "https://admin-view.invalid";
76412
- BLOCKED_PATH_RE = /^\/(?:login|admin-view)/i;
76413
- }
76414
- });
76415
-
76416
- // ../shared/dist/auth-plan.js
76417
- function hasInlineLoginSteps(playwrightCode) {
76418
- if (!playwrightCode)
76419
- return false;
76420
- return /process\.env\.TEST_USER_EMAIL!?/.test(playwrightCode) && /process\.env\.TEST_USER_PASSWORD!?/.test(playwrightCode);
76421
- }
76422
- function hasGeneratedAuthGuard(playwrightCode) {
76423
- if (!playwrightCode)
76424
- return false;
76425
- return /\/\/ >>> VALIDATE_QA_AUTH v(\d+)\b[\s\S]*?process\.env\.AUTH_FILE[\s\S]*?\/\/ <<< VALIDATE_QA_AUTH v\1\b/.test(playwrightCode);
76426
- }
76427
- function legacyNeedsCookieState(tags, code, sharedAuthInjectEnabled) {
76428
- if (!sharedAuthInjectEnabled)
76429
- return false;
76430
- if (tags.includes("@auth"))
76431
- return false;
76432
- if (tags.includes("@api"))
76433
- return true;
76434
- if (!tags.includes("@needs-auth"))
76435
- return false;
76436
- if (sharedAuthInjectEnabled && tags.includes("@auth-guarded") && hasGeneratedAuthGuard(code))
76437
- return true;
76438
- const isGeneratedVariant = tags.includes("@edge") || tags.includes("@bug");
76439
- const hasLegacyInlineLogin = tags.includes("@discovered") && !isGeneratedVariant && hasInlineLoginSteps(code);
76440
- return !tags.includes("@inline-login") && !hasLegacyInlineLogin;
76441
- }
76442
- function codeReadsStoredCredentials(code) {
76443
- if (!code)
76444
- return false;
76445
- if (/__vqaLogin\b/.test(code))
76446
- return true;
76447
- for (const m of code.matchAll(/TEST_[A-Z0-9_]+/g)) {
76448
- if (/_(?:EMAIL|PASSWORD)/.test(m[0]))
76449
- return true;
76450
- }
76451
- for (const m of code.matchAll(/process\.env\s*\[([^\]]*)\]?/g)) {
76452
- if (!/^\s*(['"])[^'"`]*\1\s*$/.test(m[1]))
76453
- return true;
76454
- }
76455
- return false;
76456
- }
76457
- function explicitAuthTypeTags(t) {
76458
- const found = /* @__PURE__ */ new Set();
76459
- for (const tag of t) {
76460
- if (AUTH_TYPE_TAGS.includes(tag))
76461
- found.add(tag);
76462
- else if (Object.hasOwn(AUTH_TAG_ALIASES, tag))
76463
- found.add(AUTH_TAG_ALIASES[tag]);
76464
- }
76465
- return [...found];
76466
- }
76467
- function invalidPlan(configurationError) {
76468
- return {
76469
- version: 1,
76470
- source: "invalid",
76471
- acquisition: "unsupported",
76472
- presentation: "none",
76473
- needsCookieState: false,
76474
- needsStandingHeaders: false,
76475
- standingHeadersMode: "none",
76476
- needsLoginCreds: false,
76477
- requiredEnvKeys: [],
76478
- configurationError
76479
- };
76480
- }
76481
- function explicitPlan(authTypeTag2, acquisition, presentation, opts) {
76482
- return {
76483
- version: 1,
76484
- source: "explicit",
76485
- authTypeTag: authTypeTag2,
76486
- acquisition,
76487
- presentation,
76488
- needsCookieState: opts.cookie,
76489
- needsStandingHeaders: opts.standingMode === "required",
76490
- standingHeadersMode: opts.standingMode,
76491
- needsLoginCreds: TEST_USER_ENV_KEYS.every((k) => opts.requiredEnvKeys.includes(k)),
76492
- requiredEnvKeys: opts.requiredEnvKeys
76493
- };
76494
- }
76495
- function deriveAuthPlan(tags, code, opts) {
76496
- const t = Array.isArray(tags) ? tags : [];
76497
- const sharedFlag = opts?.sharedAuthInjectEnabled ?? false;
76498
- const isApi = t.includes("@api");
76499
- const explicit = isApi ? explicitAuthTypeTags(t) : [];
76500
- if (explicit.length > 0) {
76501
- if (t.includes("@auth")) {
76502
- return invalidPlan("@auth (self-login UI) cannot be combined with an explicit @api auth-type tag");
76503
- }
76504
- const loginCompose = explicit.filter((e) => e === AUTH_TAG_COOKIE_STATE || e === AUTH_TAG_LOGIN_COOKIE || e === AUTH_TAG_LOGIN_BEARER);
76505
- if (explicit.length === 2 && explicit.includes(AUTH_TAG_STANDING) && loginCompose.length === 1) {
76506
- const composedKeys = [...TEST_USER_ENV_KEYS, API_AUTH_HEADERS_ENV_KEY];
76507
- switch (loginCompose[0]) {
76508
- case AUTH_TAG_COOKIE_STATE:
76509
- return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "required", requiredEnvKeys: composedKeys });
76510
- case AUTH_TAG_LOGIN_COOKIE:
76511
- return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
76512
- case AUTH_TAG_LOGIN_BEARER:
76513
- return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
76514
- }
76515
- }
76516
- if (explicit.length > 1) {
76517
- return invalidPlan(`conflicting auth-type tags: ${explicit.join(", ")}`);
76518
- }
76519
- switch (explicit[0]) {
76520
- case AUTH_TAG_UNSUPPORTED:
76521
- return {
76522
- ...invalidPlan("auth scheme not yet drivable (e.g. HMAC / mTLS / provider-replay) \u2014 needs manual configuration")
76523
- };
76524
- case AUTH_TAG_NONE:
76525
- return explicitPlan(AUTH_TAG_NONE, "none", "none", { cookie: false, standingMode: "none", requiredEnvKeys: [] });
76526
- case AUTH_TAG_STANDING:
76527
- return explicitPlan(AUTH_TAG_STANDING, "standing-secret", "standing-header", { cookie: false, standingMode: "required", requiredEnvKeys: [API_AUTH_HEADERS_ENV_KEY] });
76528
- case AUTH_TAG_LOGIN_BEARER:
76529
- return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "none", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
76530
- case AUTH_TAG_LOGIN_COOKIE:
76531
- return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
76532
- case AUTH_TAG_COOKIE_STATE:
76533
- return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
76534
- }
76693
+ return true;
76694
+ const site = siteOf(app);
76695
+ return endsWithLabel(target, site) && siteOf(target) === site;
76696
+ });
76697
+ }
76698
+ function onSharedPlatform(host) {
76699
+ return isOnSharedPlatform(host);
76700
+ }
76701
+ function namesASite(host) {
76702
+ return hostNamesASite(host);
76703
+ }
76704
+ function hostOf(url) {
76705
+ if (!url)
76706
+ return null;
76707
+ try {
76708
+ return new URL(url).hostname.toLowerCase();
76709
+ } catch {
76710
+ return null;
76535
76711
  }
76536
- const cookie = legacyNeedsCookieState(t, code, sharedFlag);
76537
- const authedLegacy = t.includes("@auth") || t.includes("@needs-auth") || isApi;
76538
- const selfLoginWithoutStoredCreds = t.includes("@auth") && !t.includes("@needs-auth") && !isApi && !!code?.trim() && !/TEST_USER_EMAIL|TEST_USER_PASSWORD|__vqaLogin\b/.test(code);
76539
- const apiWithoutStoredCreds = isApi && !t.includes("@auth") && !t.includes("@needs-auth") && !!code?.trim() && !codeReadsStoredCredentials(code);
76540
- const needsLoginCreds = authedLegacy && !selfLoginWithoutStoredCreds && !apiWithoutStoredCreds;
76541
- return {
76542
- version: 1,
76543
- source: "legacy",
76544
- acquisition: cookie ? "browser-bootstrap" : authedLegacy ? "request-login" : "none",
76545
- presentation: authedLegacy ? "cookie" : "none",
76546
- needsCookieState: cookie,
76547
- needsStandingHeaders: false,
76548
- // A legacy bare-@api test keeps injecting standing headers IF the project configured them
76549
- // (preserving today's behavior) — but does not require them. Non-@api legacy tests never inject.
76550
- standingHeadersMode: isApi ? "legacy-if-configured" : "none",
76551
- needsLoginCreds,
76552
- requiredEnvKeys: needsLoginCreds ? [...TEST_USER_ENV_KEYS] : []
76553
- };
76554
76712
  }
76555
- function authTypeTag(tags) {
76556
- const t = Array.isArray(tags) ? tags : [];
76557
- const explicit = explicitAuthTypeTags(t);
76558
- return explicit.length === 1 ? explicit[0] : void 0;
76713
+ function projectAppHosts(baseUrls) {
76714
+ const hosts = baseUrls.map(hostOf).filter((host) => !!host);
76715
+ return [...new Set(hosts)];
76559
76716
  }
76560
- 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;
76561
- var init_auth_plan = __esm({
76562
- "../shared/dist/auth-plan.js"() {
76717
+ var SHARED_AUTH_SUFFIXES;
76718
+ var init_google_app_site = __esm({
76719
+ "../shared/dist/google-app-site.js"() {
76563
76720
  "use strict";
76564
- AUTH_TAG_COOKIE_STATE = "@auth-cookie-state";
76565
- AUTH_TAG_LOGIN_COOKIE = "@auth-login-cookie";
76566
- AUTH_TAG_LOGIN_BEARER = "@auth-login-bearer";
76567
- AUTH_TAG_STANDING = "@auth-standing";
76568
- AUTH_TAG_NONE = "@auth-none";
76569
- AUTH_TAG_UNSUPPORTED = "@auth-unsupported";
76570
- AUTH_TYPE_TAGS = [
76571
- AUTH_TAG_COOKIE_STATE,
76572
- AUTH_TAG_LOGIN_COOKIE,
76573
- AUTH_TAG_LOGIN_BEARER,
76574
- AUTH_TAG_STANDING,
76575
- AUTH_TAG_NONE,
76576
- AUTH_TAG_UNSUPPORTED
76721
+ init_origin_domain();
76722
+ SHARED_AUTH_SUFFIXES = [
76723
+ "auth0.com",
76724
+ "clerk.accounts.dev",
76725
+ "supabase.co",
76726
+ "firebaseapp.com",
76727
+ "web.app",
76728
+ "amazoncognito.com",
76729
+ "stytch.com",
76730
+ "kinde.com"
76577
76731
  ];
76578
- AUTH_TAG_ALIASES = {
76579
- "@auth-cookie": AUTH_TAG_COOKIE_STATE,
76580
- "@auth-bearer": AUTH_TAG_LOGIN_BEARER,
76581
- "@auth-token": AUTH_TAG_LOGIN_BEARER,
76582
- "@auth-api-key": AUTH_TAG_STANDING,
76583
- "@auth-standing-header": AUTH_TAG_STANDING
76584
- };
76585
- TEST_USER_ENV_KEYS = ["TEST_USER_EMAIL", "TEST_USER_PASSWORD"];
76586
- API_AUTH_HEADERS_ENV_KEY = "API_AUTH_HEADERS";
76732
+ }
76733
+ });
76734
+
76735
+ // ../shared/dist/google-broker-lines.js
76736
+ function googleBrokerUnavailableLine(lines, testError) {
76737
+ if (!TIMEOUT_ERROR.test(testError ?? ""))
76738
+ return null;
76739
+ let last = null;
76740
+ for (const raw of lines ?? []) {
76741
+ for (const line of String(raw).split("\n")) {
76742
+ const text = line.trim();
76743
+ if (GOOGLE_BROKER_UNAVAILABLE.test(text) || GOOGLE_BROKER_ASKED.test(text))
76744
+ last = text;
76745
+ else if (GOOGLE_SIGNED_IN.test(text) || text.startsWith("[google-sign-in] "))
76746
+ last = null;
76747
+ }
76748
+ }
76749
+ return last;
76750
+ }
76751
+ function stepConsoleLines(result) {
76752
+ const steps = Array.isArray(result) ? result : result && typeof result === "object" && Array.isArray(result.stepResults) ? result.stepResults : [];
76753
+ return steps.flatMap((step) => {
76754
+ const logs = step && typeof step === "object" ? step.consoleLogs : null;
76755
+ return Array.isArray(logs) ? logs.filter((line) => typeof line === "string") : [];
76756
+ });
76757
+ }
76758
+ var GOOGLE_BROKER_UNAVAILABLE, GOOGLE_BROKER_ASKED, GOOGLE_SIGNED_IN, TIMEOUT_ERROR;
76759
+ var init_google_broker_lines = __esm({
76760
+ "../shared/dist/google-broker-lines.js"() {
76761
+ "use strict";
76762
+ GOOGLE_BROKER_UNAVAILABLE = /^\[google-sign-in\] (?:(?:busy|rate_limited|timeout|offline): |failed: Google sign-in service (?:answered 50[234]\b|unreachable))/;
76763
+ GOOGLE_BROKER_ASKED = /^\[google-sign-in\] the app started Google sign-in; asking the broker/;
76764
+ GOOGLE_SIGNED_IN = /^\[google-sign-in\] returned to the app with Google sign-in/;
76765
+ TIMEOUT_ERROR = /Test timeout of \d+ms exceeded|TimeoutError|Timeout:? \d+ ?ms/i;
76766
+ }
76767
+ });
76768
+
76769
+ // ../shared/dist/admin-view-return-to.js
76770
+ function sanitizeAdminViewReturnTo(value) {
76771
+ if (typeof value !== "string")
76772
+ return ADMIN_VIEW_DEFAULT_RETURN_TO;
76773
+ const trimmed = value.trim();
76774
+ if (!trimmed || trimmed.length > MAX_RETURN_TO_LENGTH)
76775
+ return ADMIN_VIEW_DEFAULT_RETURN_TO;
76776
+ if (!trimmed.startsWith("/") || trimmed.startsWith("//"))
76777
+ return ADMIN_VIEW_DEFAULT_RETURN_TO;
76778
+ if (trimmed.includes("\\") || /[\s\u0000-\u001f\u007f]/.test(trimmed))
76779
+ return ADMIN_VIEW_DEFAULT_RETURN_TO;
76780
+ let url;
76781
+ try {
76782
+ url = new URL(trimmed, PLACEHOLDER_ORIGIN);
76783
+ } catch {
76784
+ return ADMIN_VIEW_DEFAULT_RETURN_TO;
76785
+ }
76786
+ if (url.origin !== PLACEHOLDER_ORIGIN)
76787
+ return ADMIN_VIEW_DEFAULT_RETURN_TO;
76788
+ if (BLOCKED_PATH_RE.test(url.pathname))
76789
+ return ADMIN_VIEW_DEFAULT_RETURN_TO;
76790
+ const out = `${url.pathname}${url.search}${url.hash}`;
76791
+ if (out.startsWith("//") || out.includes("\\"))
76792
+ return ADMIN_VIEW_DEFAULT_RETURN_TO;
76793
+ return out;
76794
+ }
76795
+ var ADMIN_VIEW_DEFAULT_RETURN_TO, MAX_RETURN_TO_LENGTH, PLACEHOLDER_ORIGIN, BLOCKED_PATH_RE;
76796
+ var init_admin_view_return_to = __esm({
76797
+ "../shared/dist/admin-view-return-to.js"() {
76798
+ "use strict";
76799
+ ADMIN_VIEW_DEFAULT_RETURN_TO = "/sessions";
76800
+ MAX_RETURN_TO_LENGTH = 2048;
76801
+ PLACEHOLDER_ORIGIN = "https://admin-view.invalid";
76802
+ BLOCKED_PATH_RE = /^\/(?:login|admin-view)/i;
76587
76803
  }
76588
76804
  });
76589
76805
 
@@ -76594,13 +76810,16 @@ function stripEchoLines(text) {
76594
76810
  function matchesAny(text, patterns) {
76595
76811
  return patterns.some((p) => p.test(text));
76596
76812
  }
76813
+ function isIntentionalNegativeAuthTest(input) {
76814
+ const meta = [input.testName, input.description].filter(Boolean).join(" ");
76815
+ return matchesAny(meta, INTENTIONAL_NEGATIVE_AUTH);
76816
+ }
76597
76817
  function detectAuthBlock(input) {
76598
76818
  const tags = input.tags ?? [];
76599
76819
  const isAuthed = tags.some((t) => AUTHED_TAGS.includes(t));
76600
76820
  if (!isAuthed)
76601
76821
  return NO_DETECTION;
76602
- const meta = [input.testName, input.description].filter(Boolean).join(" ");
76603
- if (matchesAny(meta, INTENTIONAL_NEGATIVE_AUTH))
76822
+ if (isIntentionalNegativeAuthTest(input))
76604
76823
  return NO_DETECTION;
76605
76824
  const sources = [];
76606
76825
  if (input.error)
@@ -76703,7 +76922,7 @@ var init_auth_block_detector = __esm({
76703
76922
  /\b(reject|deny|denied|block(ed)?)\b[^\n]{0,20}\b(login|sign[\s-]?in|auth|credential|password)\b/i
76704
76923
  ];
76705
76924
  ECHO_LINE_RE = /^\s*(?:(?:📋|🎭|🌐|⚙️?)|(?:stderr:\s*|stdout:\s*)?Locator:)/u;
76706
- 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;
76925
+ 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;
76707
76926
  }
76708
76927
  });
76709
76928
 
@@ -77644,12 +77863,70 @@ function isSharedAuthInjectEnabled(value = process.env.SHARED_AUTH_INJECT) {
77644
77863
  function isAuthReadinessGuardEnabled(value = process.env.AUTH_READINESS_GUARD) {
77645
77864
  return value?.trim().toLowerCase() !== "false";
77646
77865
  }
77866
+ function visualBaselinesEnabled(projectId, raw = process.env.VISUAL_BASELINES_PROJECTS) {
77867
+ if (typeof projectId !== "string" || projectId.trim() === "")
77868
+ return false;
77869
+ const listed = (raw ?? "").split(",").map((entry) => entry.trim()).filter(Boolean);
77870
+ return listed.includes("*") || listed.includes(projectId);
77871
+ }
77647
77872
  var init_feature_flags = __esm({
77648
77873
  "../shared/dist/feature-flags.js"() {
77649
77874
  "use strict";
77650
77875
  }
77651
77876
  });
77652
77877
 
77878
+ // ../shared/dist/visual-compare-limits.js
77879
+ function isSafeMaskSelector(selector) {
77880
+ if (typeof selector !== "string")
77881
+ return false;
77882
+ if (selector.trim().length === 0)
77883
+ return false;
77884
+ if (selector.length > VISUAL_MASK_SELECTOR_MAX_LENGTH)
77885
+ return false;
77886
+ return !VISUAL_MASK_SELECTOR_FORBIDDEN.test(selector);
77887
+ }
77888
+ var VISUAL_MASK_SELECTORS_MAX, VISUAL_MASK_SELECTOR_MAX_LENGTH, VISUAL_MASK_SELECTOR_FORBIDDEN;
77889
+ var init_visual_compare_limits = __esm({
77890
+ "../shared/dist/visual-compare-limits.js"() {
77891
+ "use strict";
77892
+ VISUAL_MASK_SELECTORS_MAX = 50;
77893
+ VISUAL_MASK_SELECTOR_MAX_LENGTH = 300;
77894
+ VISUAL_MASK_SELECTOR_FORBIDDEN = /[{};@<\r\n]/;
77895
+ }
77896
+ });
77897
+
77898
+ // ../shared/dist/visual-baseline-identity.js
77899
+ function isBrowserVersion(value) {
77900
+ return typeof value === "string" && value.length <= 32 && BROWSER_VERSION.test(value);
77901
+ }
77902
+ function renderIdentity(flavour, version, image) {
77903
+ if (!/^[a-z][a-z-]{0,31}$/.test(flavour) || !/^[a-z][a-z-]{0,31}$/.test(image) || !isBrowserVersion(version))
77904
+ return null;
77905
+ const identity = `${flavour}/${version}/${image}`;
77906
+ return identity.length <= RENDER_IDENTITY_MAX_LENGTH ? identity : null;
77907
+ }
77908
+ function v1RenderIdentity(version) {
77909
+ return renderIdentity(VISUAL_V1_IDENTITY.flavour, version, VISUAL_V1_IDENTITY.image);
77910
+ }
77911
+ function parseRenderIdentity(value) {
77912
+ if (typeof value !== "string" || value.length > RENDER_IDENTITY_MAX_LENGTH)
77913
+ return null;
77914
+ const parts = value.split("/");
77915
+ if (parts.length !== 3)
77916
+ return null;
77917
+ const [flavour, version, image] = parts;
77918
+ return renderIdentity(flavour, version, image) === value ? { flavour, version, image } : null;
77919
+ }
77920
+ var VISUAL_V1_IDENTITY, RENDER_IDENTITY_MAX_LENGTH, BROWSER_VERSION;
77921
+ var init_visual_baseline_identity = __esm({
77922
+ "../shared/dist/visual-baseline-identity.js"() {
77923
+ "use strict";
77924
+ VISUAL_V1_IDENTITY = { platform: "linux", image: "runner-fast", flavour: "headless-shell" };
77925
+ RENDER_IDENTITY_MAX_LENGTH = 64;
77926
+ BROWSER_VERSION = /^\d{1,4}(\.\d{1,6}){0,3}$/;
77927
+ }
77928
+ });
77929
+
77653
77930
  // ../shared/dist/analytics-url.js
77654
77931
  function isSensitiveQueryKey(key) {
77655
77932
  return SENSITIVE_QUERY_KEY_RE.test(key);
@@ -78003,14 +78280,9 @@ function isOpenCodeGoModelId(id) {
78003
78280
  function hasOpenCodeGoPrefix(id) {
78004
78281
  return (id?.trim() ?? "").startsWith(OPENCODE_GO_PREFIX);
78005
78282
  }
78006
- function hasOpenCodeGo2Prefix(id) {
78007
- return (id?.trim() ?? "").startsWith(OPENCODE_GO_2_PREFIX);
78008
- }
78009
78283
  function openCodeGoWireModel(id) {
78010
78284
  const trimmed = id?.trim() ?? "";
78011
- if (!isOpenCodeGoModelId(trimmed))
78012
- return null;
78013
- return trimmed.slice((hasOpenCodeGo2Prefix(trimmed) ? OPENCODE_GO_2_PREFIX : OPENCODE_GO_PREFIX).length);
78285
+ return isOpenCodeGoModelId(trimmed) ? trimmed.slice(OPENCODE_GO_PREFIX.length) : null;
78014
78286
  }
78015
78287
  function isZaiModelId(id) {
78016
78288
  const trimmed = id?.trim() ?? "";
@@ -78119,7 +78391,7 @@ function museCliContributorMode(env = process.env) {
78119
78391
  const raw = (env.MUSE_CLI_CONTRIBUTOR_MODE ?? "").trim().toLowerCase();
78120
78392
  return raw === "off" || raw === "false" || raw === "0" || raw === "no" ? "off" : "on";
78121
78393
  }
78122
- var CLI_ENGINE_MODEL_IDS, BROWSER_CLI_ENGINE_IDS, PIPELINE_STEPS, CLI_ENGINE_RULES, ENGINE_NAMES, OPENROUTER_PREFIX, OPENROUTER_MODEL_PATTERN, OPENCODE_GO_PREFIX, OPENCODE_GO_2_PREFIX, OPENCODE_GO_MODEL_PATTERN, ZAI_PREFIX, ZAI_MODEL_PATTERN;
78394
+ 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;
78123
78395
  var init_engine_spec = __esm({
78124
78396
  "../shared/dist/engine-spec.js"() {
78125
78397
  "use strict";
@@ -78178,8 +78450,7 @@ var init_engine_spec = __esm({
78178
78450
  OPENROUTER_PREFIX = "openrouter:";
78179
78451
  OPENROUTER_MODEL_PATTERN = /^openrouter:[a-z0-9][a-z0-9._-]{0,40}\/[A-Za-z0-9._:-]{1,100}$/;
78180
78452
  OPENCODE_GO_PREFIX = "opencode-go:";
78181
- OPENCODE_GO_2_PREFIX = "opencode-go-2:";
78182
- OPENCODE_GO_MODEL_PATTERN = /^opencode-go(?:-2)?:[a-z0-9][a-z0-9._-]{0,100}$/;
78453
+ OPENCODE_GO_MODEL_PATTERN = /^opencode-go:[a-z0-9][a-z0-9._-]{0,100}$/;
78183
78454
  ZAI_PREFIX = "zai:";
78184
78455
  ZAI_MODEL_PATTERN = /^zai:glm-[a-z0-9][a-z0-9._-]{0,60}$/;
78185
78456
  }
@@ -78504,14 +78775,19 @@ function has(map2, key) {
78504
78775
  }
78505
78776
  function providerChainFor(modelId) {
78506
78777
  const key = chainKey(modelId);
78778
+ if (key && has(_overrideHops, key))
78779
+ return _overrideHops[key];
78507
78780
  return key && has(PROVIDER_CHAINS, key) ? PROVIDER_CHAINS[key] : [];
78508
78781
  }
78509
78782
  function providerChainLeadsFor(modelId) {
78510
78783
  const key = chainKey(modelId);
78784
+ if (key && has(_overrideLeads, key))
78785
+ return _overrideLeads[key];
78511
78786
  return key && has(PROVIDER_CHAIN_LEADS, key) ? PROVIDER_CHAIN_LEADS[key] : [];
78512
78787
  }
78513
78788
  function allChains() {
78514
- return [...Object.entries(PROVIDER_CHAINS), ...Object.entries(PROVIDER_CHAIN_LEADS)];
78789
+ const ordered = (map2, override) => [...Object.entries(map2).filter(([key]) => !has(override, key)), ...Object.entries(override)];
78790
+ return [...ordered(PROVIDER_CHAINS, _overrideHops), ...ordered(PROVIDER_CHAIN_LEADS, _overrideLeads)];
78515
78791
  }
78516
78792
  function isProviderChainHopId(modelId) {
78517
78793
  const id = modelId ?? "";
@@ -78528,14 +78804,24 @@ function providerChainHostOf(value, modelValue) {
78528
78804
  return "own";
78529
78805
  if (hasZaiPrefix(value))
78530
78806
  return "zai";
78531
- if (hasOpenCodeGo2Prefix(value))
78532
- return "opencode-go-2";
78533
78807
  if (hasOpenCodeGoPrefix(value))
78534
78808
  return "opencode-go";
78535
78809
  if (hasOpenRouterPrefix(value))
78536
78810
  return "openrouter";
78537
78811
  return "own";
78538
78812
  }
78813
+ function providerChainHostProvider(value) {
78814
+ if (typeof value !== "string" || !value)
78815
+ return null;
78816
+ if (hasOpenRouterPrefix(value))
78817
+ return "openrouter";
78818
+ if (hasOpenCodeGoPrefix(value))
78819
+ return "opencode-go";
78820
+ if (hasZaiPrefix(value))
78821
+ return "zai";
78822
+ const key = chainKey(value);
78823
+ return API_MODEL_CATALOG.find((m) => m.id === key)?.provider ?? GEMINI_ASSISTANT_MODELS.find((m) => m.id === key)?.provider ?? null;
78824
+ }
78539
78825
  function providerHostPlan(modelId) {
78540
78826
  const key = chainKey(modelId);
78541
78827
  if (!key)
@@ -78553,7 +78839,140 @@ function providerHostPlan(modelId) {
78553
78839
  }
78554
78840
  return steps;
78555
78841
  }
78556
- var PROVIDER_CHAINS, PROVIDER_CHAIN_LEADS, PROVIDER_HOST_NOT_OFFERED;
78842
+ function providerChainModelKey(modelId) {
78843
+ return chainKey(modelId);
78844
+ }
78845
+ function hasProviderChainLeadsByDefault(modelId) {
78846
+ const key = chainKey(modelId);
78847
+ return key !== null && has(PROVIDER_CHAIN_LEADS, key);
78848
+ }
78849
+ function isChainablePrimary(id) {
78850
+ return typeof id === "string" && id !== "" && (has(PROVIDER_CHAINS, id) || has(PROVIDER_CHAIN_LEADS, id));
78851
+ }
78852
+ function defaultProviderChainEntries(primary) {
78853
+ if (!isChainablePrimary(primary))
78854
+ return [];
78855
+ const leads = has(PROVIDER_CHAIN_LEADS, primary) ? PROVIDER_CHAIN_LEADS[primary] : [];
78856
+ const hops = has(PROVIDER_CHAINS, primary) ? PROVIDER_CHAINS[primary] : [];
78857
+ return [...leads, primary, ...hops].map((hop) => ({ hop, enabled: true }));
78858
+ }
78859
+ function allowedProviderChainHosts(primary) {
78860
+ return defaultProviderChainEntries(primary).map((entry) => entry.hop);
78861
+ }
78862
+ function isPlainObject2(value) {
78863
+ return value !== null && typeof value === "object" && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
78864
+ }
78865
+ function validateProviderChainEntries(primary, entries) {
78866
+ if (!isChainablePrimary(primary))
78867
+ return `${String(primary).slice(0, 80)} has no provider chain to order`;
78868
+ if (!Array.isArray(entries))
78869
+ return `${primary}: the chain must be a list`;
78870
+ if (entries.length > PROVIDER_CHAIN_MAX_ENTRIES)
78871
+ return `${primary}: at most ${PROVIDER_CHAIN_MAX_ENTRIES} hosts`;
78872
+ const allowed = allowedProviderChainHosts(primary);
78873
+ const seen = /* @__PURE__ */ new Set();
78874
+ for (const entry of entries) {
78875
+ if (!isPlainObject2(entry))
78876
+ return `${primary}: each host must be { hop, enabled }`;
78877
+ const keys = Object.keys(entry);
78878
+ if (keys.length !== 2 || !keys.includes("hop") || !keys.includes("enabled"))
78879
+ return `${primary}: each host must be { hop, enabled } and nothing else`;
78880
+ const { hop, enabled } = entry;
78881
+ if (typeof enabled !== "boolean")
78882
+ return `${primary}: enabled must be true or false`;
78883
+ if (typeof hop !== "string" || !hop)
78884
+ return `${primary}: hop must be a model value`;
78885
+ if (!allowed.includes(hop))
78886
+ return `${primary}: ${hop.slice(0, 80)} is not a host of this model`;
78887
+ if (seen.has(hop))
78888
+ return `${primary}: ${hop} is listed twice`;
78889
+ if (hop === primary && !enabled)
78890
+ return `${primary}: the model's own value stays on (pick another model on AI Models instead)`;
78891
+ seen.add(hop);
78892
+ }
78893
+ if (!seen.has(primary))
78894
+ return `${primary}: list the model's own value once, where its own host sits`;
78895
+ return null;
78896
+ }
78897
+ function decodeProviderChains(raw) {
78898
+ let value = raw;
78899
+ if (typeof raw === "string") {
78900
+ if (raw.length > PROVIDER_CHAINS_SETTING_MAX_CHARS)
78901
+ return { ok: false, error: `AI_PROVIDER_CHAINS is too long (at most ${PROVIDER_CHAINS_SETTING_MAX_CHARS} characters)` };
78902
+ try {
78903
+ value = JSON.parse(raw);
78904
+ } catch {
78905
+ return { ok: false, error: "AI_PROVIDER_CHAINS must be valid JSON" };
78906
+ }
78907
+ } else if (JSON.stringify(raw ?? null).length > PROVIDER_CHAINS_SETTING_MAX_CHARS) {
78908
+ return { ok: false, error: `AI_PROVIDER_CHAINS is too long (at most ${PROVIDER_CHAINS_SETTING_MAX_CHARS} characters)` };
78909
+ }
78910
+ if (!isPlainObject2(value))
78911
+ return { ok: false, error: 'AI_PROVIDER_CHAINS must be an object: { "<model>": [ { "hop": "<model value>", "enabled": true } ] }' };
78912
+ if (Object.keys(value).length > PROVIDER_CHAIN_MAX_PRIMARIES)
78913
+ return { ok: false, error: `AI_PROVIDER_CHAINS names at most ${PROVIDER_CHAIN_MAX_PRIMARIES} models` };
78914
+ return { ok: true, value };
78915
+ }
78916
+ function checkedProviderChains(value) {
78917
+ const out = {};
78918
+ for (const [primary, entries] of Object.entries(value)) {
78919
+ const problem = validateProviderChainEntries(primary, entries);
78920
+ if (problem)
78921
+ return { ok: false, error: problem };
78922
+ out[primary] = entries.map(({ hop, enabled }) => ({ hop, enabled }));
78923
+ }
78924
+ return { ok: true, value: out };
78925
+ }
78926
+ function parseProviderChainOverrides(raw) {
78927
+ const decoded = decodeProviderChains(raw);
78928
+ return decoded.ok ? checkedProviderChains(decoded.value) : decoded;
78929
+ }
78930
+ function parseStoredProviderChainOverrides(raw) {
78931
+ const decoded = decodeProviderChains(raw);
78932
+ if (!decoded.ok)
78933
+ return decoded;
78934
+ const dropped = [];
78935
+ const kept = {};
78936
+ for (const [primary, entries] of Object.entries(decoded.value)) {
78937
+ if (!isChainablePrimary(primary) || !Array.isArray(entries)) {
78938
+ kept[primary] = entries;
78939
+ continue;
78940
+ }
78941
+ const allowed = allowedProviderChainHosts(primary);
78942
+ kept[primary] = entries.filter((entry) => {
78943
+ const hop = isPlainObject2(entry) ? entry.hop : void 0;
78944
+ if (typeof hop !== "string" || hop === "" || allowed.includes(hop))
78945
+ return true;
78946
+ dropped.push({ primary, hop: hop.slice(0, 80) });
78947
+ return false;
78948
+ });
78949
+ }
78950
+ const checked = checkedProviderChains(kept);
78951
+ return checked.ok ? { ...checked, dropped } : checked;
78952
+ }
78953
+ function validateProviderChainsSetting(raw) {
78954
+ const parsed = parseProviderChainOverrides(raw);
78955
+ return parsed.ok ? null : parsed.error;
78956
+ }
78957
+ function setProviderChainOverrides(value, onDropped) {
78958
+ const parsed = value === null || value === void 0 || value === "" ? null : parseStoredProviderChainOverrides(value);
78959
+ if (parsed?.ok && parsed.dropped.length > 0)
78960
+ onDropped?.(parsed.dropped);
78961
+ _overrides = parsed?.ok ? parsed.value : null;
78962
+ _overrideLeads = {};
78963
+ _overrideHops = {};
78964
+ for (const [primary, entries] of Object.entries(_overrides ?? {})) {
78965
+ const at = entries.findIndex((entry) => entry.hop === primary);
78966
+ const on = (list) => list.filter((entry) => entry.enabled).map((entry) => entry.hop);
78967
+ _overrideLeads[primary] = on(entries.slice(0, at));
78968
+ _overrideHops[primary] = on(entries.slice(at + 1));
78969
+ }
78970
+ return parsed === null || parsed.ok;
78971
+ }
78972
+ function getProviderChainOverrides() {
78973
+ return _overrides;
78974
+ }
78975
+ 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;
78557
78976
  var init_provider_chains = __esm({
78558
78977
  "../shared/dist/provider-chains.js"() {
78559
78978
  "use strict";
@@ -78565,18 +78984,120 @@ var init_provider_chains = __esm({
78565
78984
  "muse-spark-1.3-contributor": ["openrouter:meta/muse-spark-1.3-contributor"],
78566
78985
  "claude-haiku-5-5": ["opencode-go:claude-haiku-5-5", "openrouter:anthropic/claude-haiku-5.5"],
78567
78986
  "gemini-3.1-pro-preview": ["openrouter:google/gemini-3.1-pro-preview"],
78568
- "deepseek-flash": ["opencode-go-2:deepseek-v4.1-flash", "opencode-go:deepseek-v4.1-flash", "openrouter:deepseek/deepseek-v4.1-flash"],
78569
- "deepseek-v4-pro": ["opencode-go-2:deepseek-v4-pro", "opencode-go:deepseek-v4-pro", "openrouter:deepseek/deepseek-v4-pro"]
78987
+ "deepseek-flash": ["opencode-go:deepseek-v4.1-flash", "openrouter:deepseek/deepseek-v4.1-flash"],
78988
+ "deepseek-v4-pro": ["opencode-go:deepseek-v4-pro", "openrouter:deepseek/deepseek-v4-pro"]
78570
78989
  };
78571
78990
  PROVIDER_CHAIN_LEADS = {
78572
- "openrouter:z-ai/glm-5.3-flash": ["zai:glm-5.3-flash", "opencode-go-2:glm-5.3-flash", "opencode-go:glm-5.3-flash"],
78573
- "openrouter:z-ai/glm-5.3": ["zai:glm-5.3", "opencode-go-2:glm-5.3", "opencode-go:glm-5.3"],
78574
- "openrouter:z-ai/glm-5.2": ["zai:glm-5.2", "opencode-go-2:glm-5.2", "opencode-go:glm-5.2"]
78991
+ "openrouter:z-ai/glm-5.3-flash": ["zai:glm-5.3-flash", "opencode-go:glm-5.3-flash"],
78992
+ "openrouter:z-ai/glm-5.3": ["zai:glm-5.3", "opencode-go:glm-5.3"],
78993
+ "openrouter:z-ai/glm-5.2": ["zai:glm-5.2", "opencode-go:glm-5.2"]
78575
78994
  };
78576
78995
  PROVIDER_HOST_NOT_OFFERED = {
78577
78996
  "muse-spark-1.3-contributor": { "opencode-go": "OpenCode Go serves Muse only on its Responses API" },
78578
78997
  "gemini-3.1-pro-preview": { "opencode-go": "OpenCode Go does not serve Gemini" }
78579
78998
  };
78999
+ PROVIDER_CHAIN_MAX_ENTRIES = 8;
79000
+ PROVIDER_CHAINS_SETTING_MAX_CHARS = 16e3;
79001
+ PROVIDER_CHAIN_MAX_PRIMARIES = 32;
79002
+ ALLOWED_PROVIDER_CHAIN_HOPS = /* @__PURE__ */ new Set([
79003
+ ...Object.values(PROVIDER_CHAINS).flat(),
79004
+ ...Object.values(PROVIDER_CHAIN_LEADS).flat()
79005
+ ]);
79006
+ _overrides = null;
79007
+ _overrideLeads = {};
79008
+ _overrideHops = {};
79009
+ }
79010
+ });
79011
+
79012
+ // ../shared/dist/provider-accounts.js
79013
+ function modelNames(modelId) {
79014
+ const value = modelId.trim();
79015
+ const names = /* @__PURE__ */ new Set([value]);
79016
+ for (const prefix of HOST_PREFIXES) {
79017
+ if (!value.startsWith(prefix))
79018
+ continue;
79019
+ const bare = value.slice(prefix.length);
79020
+ names.add(bare);
79021
+ const slash = bare.lastIndexOf("/");
79022
+ if (slash >= 0)
79023
+ names.add(bare.slice(slash + 1));
79024
+ }
79025
+ const canonical = canonicalModelId(value);
79026
+ if (canonical) {
79027
+ names.add(canonical);
79028
+ const entry = API_MODEL_CATALOG.find((m) => m.id === canonical || m.apiModel === canonical);
79029
+ if (entry) {
79030
+ names.add(entry.id);
79031
+ if (entry.apiModel)
79032
+ names.add(entry.apiModel);
79033
+ }
79034
+ }
79035
+ return names;
79036
+ }
79037
+ function accountServesModel(models, modelId) {
79038
+ if (!models || models.length === 0)
79039
+ return true;
79040
+ if (!modelId)
79041
+ return false;
79042
+ const names = modelNames(modelId);
79043
+ return models.some((model) => names.has(model.trim()));
79044
+ }
79045
+ function normalizeProviderAccountModels(models) {
79046
+ if (!Array.isArray(models))
79047
+ return null;
79048
+ const out = [];
79049
+ for (const model of models) {
79050
+ if (typeof model !== "string")
79051
+ return null;
79052
+ const trimmed = model.trim();
79053
+ if (!PROVIDER_ACCOUNT_MODEL_PATTERN.test(trimmed) || trimmed.includes(".."))
79054
+ return null;
79055
+ if (!out.includes(trimmed))
79056
+ out.push(trimmed);
79057
+ }
79058
+ return out.length > MAX_PROVIDER_ACCOUNT_MODELS ? null : out;
79059
+ }
79060
+ function parseProviderAccountLists(value, providers) {
79061
+ if (!value || typeof value !== "object" || Array.isArray(value))
79062
+ return null;
79063
+ const parsed = {};
79064
+ for (const provider of providers) {
79065
+ const list = value[provider];
79066
+ if (!Array.isArray(list))
79067
+ continue;
79068
+ const accounts = [];
79069
+ for (const raw of list) {
79070
+ if (!raw || typeof raw !== "object")
79071
+ continue;
79072
+ const { id, label, key, models } = raw;
79073
+ if (typeof id !== "string" || !id.trim() || typeof key !== "string" || !key.trim())
79074
+ continue;
79075
+ const modelList = Array.isArray(models) ? models.filter((m) => typeof m === "string" && m.trim() !== "").map((m) => m.trim()) : [];
79076
+ accounts.push({ id: id.trim(), label: typeof label === "string" && label.trim() ? label.trim() : null, key: key.trim(), models: modelList });
79077
+ }
79078
+ if (accounts.length > 0)
79079
+ parsed[provider] = accounts;
79080
+ }
79081
+ return parsed;
79082
+ }
79083
+ 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;
79084
+ var init_provider_accounts = __esm({
79085
+ "../shared/dist/provider-accounts.js"() {
79086
+ "use strict";
79087
+ init_api_model_catalog();
79088
+ DEFAULT_PROVIDER_ACCOUNT_ID = "primary";
79089
+ PROVIDER_ACCOUNT_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,39}$/;
79090
+ MAX_PROVIDER_ACCOUNTS = 10;
79091
+ MAX_PROVIDER_ACCOUNT_MODELS = 50;
79092
+ PROVIDER_ACCOUNT_MODEL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,120}$/;
79093
+ OPENCODE_GO_DEEPSEEK_GLM_MODELS = [
79094
+ "deepseek-v4.1-flash",
79095
+ "deepseek-v4-pro",
79096
+ "glm-5.3-flash",
79097
+ "glm-5.3",
79098
+ "glm-5.2"
79099
+ ];
79100
+ HOST_PREFIXES = ["opencode-go:", "zai:", "openrouter:"];
78580
79101
  }
78581
79102
  });
78582
79103
 
@@ -78695,6 +79216,25 @@ var init_project_engine_overrides = __esm({
78695
79216
  }
78696
79217
  });
78697
79218
 
79219
+ // ../shared/dist/trailing-slashes.js
79220
+ function stripTrailingSlashes(value) {
79221
+ let end = value.length;
79222
+ while (end > 0 && value.charCodeAt(end - 1) === 47)
79223
+ end--;
79224
+ return end === value.length ? value : value.slice(0, end);
79225
+ }
79226
+ function stripTrailingChars(value, isTrailing) {
79227
+ let end = value.length;
79228
+ while (end > 0 && isTrailing.test(value[end - 1]))
79229
+ end--;
79230
+ return end === value.length ? value : value.slice(0, end);
79231
+ }
79232
+ var init_trailing_slashes = __esm({
79233
+ "../shared/dist/trailing-slashes.js"() {
79234
+ "use strict";
79235
+ }
79236
+ });
79237
+
78698
79238
  // ../shared/dist/gemini-visual.js
78699
79239
  function visualProDailyBudget(raw) {
78700
79240
  const parsed = raw && /^\s*\d{1,6}\s*$/.test(raw) ? Number(raw.trim()) : NaN;
@@ -78708,7 +79248,7 @@ function isRetiredGeminiModel(id) {
78708
79248
  }
78709
79249
  function geminiNativeModelsUrl(base2 = process.env.GEMINI_API_BASE) {
78710
79250
  const raw = base2 && base2.trim() || GEMINI_OPENAI_BASE;
78711
- const native = raw.replace(/\/openai\/?$/, "/").replace(/\/+$/, "");
79251
+ const native = stripTrailingSlashes(raw.replace(/\/openai\/?$/, "/"));
78712
79252
  return `${native}/models?pageSize=1000`;
78713
79253
  }
78714
79254
  function asRecord(value) {
@@ -78828,6 +79368,7 @@ var KNOWN_INPUT_USD_PER_M, GEMINI_OPENAI_BASE, DEFAULT_GEMINI_SCREEN_MODEL, DEFA
78828
79368
  var init_gemini_visual = __esm({
78829
79369
  "../shared/dist/gemini-visual.js"() {
78830
79370
  "use strict";
79371
+ init_trailing_slashes();
78831
79372
  KNOWN_INPUT_USD_PER_M = {
78832
79373
  "gemini-2.5-flash-lite": 0.1,
78833
79374
  "gemini-3.1-flash-lite": 0.25,
@@ -80191,12 +80732,13 @@ function truncateAtWord(text, max) {
80191
80732
  const slice = trimmed.slice(0, room + 1);
80192
80733
  const lastSpace = slice.lastIndexOf(" ");
80193
80734
  const cut = lastSpace > room * 0.5 ? slice.slice(0, lastSpace) : trimmed.slice(0, room);
80194
- return `${cut.replace(/[\s,;:.\-–—]+$/u, "")}\u2026`;
80735
+ return `${stripTrailingChars(cut, TRAILING_PUNCTUATION)}\u2026`;
80195
80736
  }
80196
- 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;
80737
+ 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;
80197
80738
  var init_canvas_support = __esm({
80198
80739
  "../shared/dist/canvas-support.js"() {
80199
80740
  "use strict";
80741
+ init_trailing_slashes();
80200
80742
  CANVAS_UNSUPPORTED_CODE = "canvas_unsupported";
80201
80743
  CANVAS_UNSUPPORTED_TITLE = "Games and canvas apps aren't supported yet";
80202
80744
  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.";
@@ -80321,6 +80863,7 @@ var init_canvas_support = __esm({
80321
80863
  TOUCH_NONE_RE = /touch-action\s{0,8}:\s{0,8}none/;
80322
80864
  CODE_TAGS = /* @__PURE__ */ new Set(["script", "link", "flt-glass-pane", "flutter-view", "flt-scene-host"]);
80323
80865
  STATIC_STANDARD_MIN_INTERACTIVE = 3;
80866
+ TRAILING_PUNCTUATION = /[\s,;:.\-–—]/u;
80324
80867
  }
80325
80868
  });
80326
80869
 
@@ -80503,22 +81046,22 @@ function joinChainedLines(code) {
80503
81046
  const trimmed = lines[i].trimStart();
80504
81047
  if (last !== -1 && trimmed.startsWith(".") && !trimmed.startsWith("...")) {
80505
81048
  if (!lastStripped) {
80506
- lines[last] = stripLineComment2(lines[last]).trimEnd();
81049
+ lines[last] = stripLineComment3(lines[last]).trimEnd();
80507
81050
  lastStripped = true;
80508
81051
  }
80509
81052
  if (lines[last].length <= MAX_LINE_CHARS)
80510
- lines[last] = `${lines[last]} ${stripLineComment2(trimmed).trimEnd()}`;
81053
+ lines[last] = `${lines[last]} ${stripLineComment3(trimmed).trimEnd()}`;
80511
81054
  lines[i] = "";
80512
81055
  continue;
80513
81056
  }
80514
- if (stripLineComment2(lines[i]).trim()) {
81057
+ if (stripLineComment3(lines[i]).trim()) {
80515
81058
  last = i;
80516
81059
  lastStripped = false;
80517
81060
  }
80518
81061
  }
80519
81062
  return lines.join("\n");
80520
81063
  }
80521
- function stripLineComment2(rawLine) {
81064
+ function stripLineComment3(rawLine) {
80522
81065
  if (rawLine.trimStart().startsWith("//"))
80523
81066
  return "";
80524
81067
  const at = rawLine.indexOf(" // ");
@@ -80567,7 +81110,7 @@ function analyzeReadinessPolling(rawCode) {
80567
81110
  offset += rawLine.length + 1;
80568
81111
  if (rawLine.length > MAX_LINE_CHARS)
80569
81112
  continue;
80570
- const line = stripLineComment2(rawLine);
81113
+ const line = stripLineComment3(rawLine);
80571
81114
  if (!line.trim())
80572
81115
  continue;
80573
81116
  const literals = literalsOf(line);
@@ -80679,7 +81222,7 @@ function blankBlockComments2(code) {
80679
81222
  from = close + 2;
80680
81223
  }
80681
81224
  }
80682
- function stripLineComment3(rawLine) {
81225
+ function stripLineComment4(rawLine) {
80683
81226
  if (rawLine.trimStart().startsWith("//"))
80684
81227
  return "";
80685
81228
  const at = rawLine.indexOf(" // ");
@@ -80690,7 +81233,7 @@ function logicalLines(code) {
80690
81233
  for (const raw of code.split("\n")) {
80691
81234
  if (raw.length > MAX_LINE_CHARS2)
80692
81235
  continue;
80693
- const line = stripLineComment3(raw).trimEnd();
81236
+ const line = stripLineComment4(raw).trimEnd();
80694
81237
  if (!line.trim())
80695
81238
  continue;
80696
81239
  const trimmed = line.trimStart();
@@ -81052,8 +81595,19 @@ function short4(text, max) {
81052
81595
  const flat = text.replace(/\s+/g, " ").trim();
81053
81596
  return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
81054
81597
  }
81598
+ function stripFractionZeros(s) {
81599
+ let end = s.length;
81600
+ while (end > 0 && s[end - 1] === "0")
81601
+ end--;
81602
+ if (end === s.length)
81603
+ return s;
81604
+ const dot = s.lastIndexOf(".");
81605
+ if (dot < 0)
81606
+ return s;
81607
+ return s.slice(0, end === dot + 1 ? dot : end);
81608
+ }
81055
81609
  function digitsOf(token) {
81056
- return token.replace(/[^\d.]/g, "").replace(/\.0+$|(\.\d*?)0+$/, "$1").replace(/^0+(?=\d)/, "");
81610
+ return stripFractionZeros(token.replace(/[^\d.]/g, "")).replace(/^0+(?=\d)/, "");
81057
81611
  }
81058
81612
  function computes(line) {
81059
81613
  const code = line.replace(STRING_RE, '""').replace(REGEX_LITERAL_RE, "$1RX").replace(LITERAL_ARITH_RE, "N");
@@ -81271,7 +81825,7 @@ function findExpectCalls(code) {
81271
81825
  if (!subj)
81272
81826
  continue;
81273
81827
  const rest = code.slice(subj.end + 1, subj.end + 1 + 80);
81274
- const mm = /^\s*((?:\.\s*not)?\s*\.\s*(\w+))\s{0,20}\(/.exec(rest);
81828
+ const mm = /^\s*((?:\.\s*not\s*)?\.\s*(\w+))\s{0,20}\(/.exec(rest);
81275
81829
  if (!mm)
81276
81830
  continue;
81277
81831
  const argStart = subj.end + 1 + mm[0].length;
@@ -83221,6 +83775,28 @@ function startsWithRegexLiteral(text) {
83221
83775
  const t = text.trimStart();
83222
83776
  return t[0] === "/" && t[1] !== "/" && t[1] !== "*" && regexLiteralEnd4(t, 0) !== -1;
83223
83777
  }
83778
+ function isBareRegexLiteral(text) {
83779
+ const t = text.trimStart();
83780
+ if (t[0] !== "/" || t[1] === "/" || t[1] === "*")
83781
+ return false;
83782
+ const end = regexLiteralEnd4(t, 0);
83783
+ if (end === -1)
83784
+ return false;
83785
+ let i = end;
83786
+ while (i < t.length && /[a-z]/.test(t[i]))
83787
+ i++;
83788
+ while (i < t.length && /\s/.test(t[i]))
83789
+ i++;
83790
+ return i >= t.length || t[i] === ",";
83791
+ }
83792
+ function regexDeclaredNames(code) {
83793
+ const names = /* @__PURE__ */ new Set();
83794
+ 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;
83795
+ let m;
83796
+ while ((m = decl.exec(code)) !== null)
83797
+ names.add(m[1]);
83798
+ return names;
83799
+ }
83224
83800
  function afterFirstArg(args) {
83225
83801
  let depth = 0;
83226
83802
  let quote = null;
@@ -83258,6 +83834,7 @@ function analyzeRegexMatchers(rawCode) {
83258
83834
  let examined = 0;
83259
83835
  let line = 1;
83260
83836
  let lineAt2 = 0;
83837
+ let regexNames;
83261
83838
  while ((m = re.exec(code)) !== null && examined++ < MAX_CALLS && out.length < MAX_REGEX_MATCHER_FINDINGS) {
83262
83839
  const subj = readBalanced(code, m.index + m[0].length);
83263
83840
  if (!subj)
@@ -83269,10 +83846,18 @@ function analyzeRegexMatchers(rawCode) {
83269
83846
  if (!args)
83270
83847
  continue;
83271
83848
  const expected = mm[2] === "toHaveJSProperty" ? afterFirstArg(args.text) : args.text;
83272
- if (!startsWithRegexLiteral(expected))
83273
- continue;
83274
- if (mm[2] !== "toHaveJSProperty" && mm[2] !== "toContain" && (startsWithRegexLiteral(subj.text) || /^new\s+RegExp\b/.test(subj.text.trim())))
83849
+ if (!isBareRegexLiteral(expected))
83275
83850
  continue;
83851
+ if (mm[2] !== "toHaveJSProperty" && mm[2] !== "toContain") {
83852
+ const subject = subj.text.trim();
83853
+ if (startsWithRegexLiteral(subject) || /^new\s+RegExp\b/.test(subject))
83854
+ continue;
83855
+ if (/^[A-Za-z_$][\w$]{0,80}$/.test(subject)) {
83856
+ regexNames ??= regexDeclaredNames(code);
83857
+ if (regexNames.has(subject))
83858
+ continue;
83859
+ }
83860
+ }
83276
83861
  for (let i = code.indexOf("\n", lineAt2); i !== -1 && i < m.index; i = code.indexOf("\n", i + 1)) {
83277
83862
  line++;
83278
83863
  lineAt2 = i + 1;
@@ -83282,9 +83867,12 @@ function analyzeRegexMatchers(rawCode) {
83282
83867
  return out;
83283
83868
  }
83284
83869
  function formatRegexMatcherNote(findings) {
83285
- const lines = findings.map((f) => `Line ${f.line}: .${f.negated ? "not." : ""}${f.matcher}(\u2026 /regex/ \u2026) ${f.negated ? "always passes (a regex never equals the value), so it checks nothing" : "can never pass: this matcher compares by equality and a regex never equals a value"}.`);
83870
+ const lines = findings.map((f) => {
83871
+ 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";
83872
+ 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"}.`;
83873
+ });
83286
83874
  return `${lines.join("\n")}
83287
- A regex only works in toMatch, toHaveText, toContainText, toHaveValue and similar text matchers. For a DOM property read it first: expect(await loc.evaluate((n) => n.<prop>)).toMatch(/re/). Or give toHaveJSProperty the exact string (or boolean) the page shows. Keep every other line as-is.`;
83875
+ 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.`;
83288
83876
  }
83289
83877
  function lintTestQuality(rawCode, opts = {}) {
83290
83878
  if (!rawCode || typeof rawCode !== "string")
@@ -84033,19 +84621,6 @@ var init_fence_runs = __esm({
84033
84621
  }
84034
84622
  });
84035
84623
 
84036
- // ../shared/dist/trailing-slashes.js
84037
- function stripTrailingSlashes(value) {
84038
- let end = value.length;
84039
- while (end > 0 && value.charCodeAt(end - 1) === 47)
84040
- end--;
84041
- return end === value.length ? value : value.slice(0, end);
84042
- }
84043
- var init_trailing_slashes = __esm({
84044
- "../shared/dist/trailing-slashes.js"() {
84045
- "use strict";
84046
- }
84047
- });
84048
-
84049
84624
  // ../shared/dist/survey-bug-signature.js
84050
84625
  function normalizeWords(text) {
84051
84626
  return text.toLowerCase().replace(UUID, " id ").replace(PREFIXED_ID, " id ").replace(HEX_RUN, (run) => /\d/.test(run) ? " id " : run).replace(NUMBER_RUN, " # ").replace(/[^\p{L}\p{N}#]+/gu, " ").replace(/\s+/g, " ").trim();
@@ -84069,14 +84644,17 @@ function requestSignatures(text) {
84069
84644
  const statuses = [...text.matchAll(HTTP_STATUS)].map((m) => ({ status: m[1], at: m.index ?? 0, end: (m.index ?? 0) + m[0].length }));
84070
84645
  if (statuses.length === 0)
84071
84646
  return [];
84072
- const paths = [...text.matchAll(PATH_IN_TEXT)].map((m) => ({
84073
- method: m[1] ?? null,
84074
- path: normalizeBugPath((m[2] ?? m[3] ?? "").replace(/[.,;:]+$/, "")),
84075
- // Before normalisation, which turns a segment with a digit (favicon-32x32.png) into :id.
84076
- incidental: isIncidentalAssetPath((m[2] ?? m[3] ?? "").replace(/[.,;:]+$/, "").split(/[?#]/, 1)[0].toLowerCase()),
84077
- at: m.index ?? 0,
84078
- end: (m.index ?? 0) + m[0].length
84079
- })).filter((p) => p.path.length > 1);
84647
+ const paths = [...text.matchAll(PATH_IN_TEXT)].map((m) => {
84648
+ const rawPath = stripTrailingChars(m[2] ?? m[3] ?? "", PATH_TRAILING_PUNCTUATION);
84649
+ return {
84650
+ method: m[1] ?? null,
84651
+ path: normalizeBugPath(rawPath),
84652
+ // Before normalisation, which turns a segment with a digit (favicon-32x32.png) into :id.
84653
+ incidental: isIncidentalAssetPath(rawPath.split(/[?#]/, 1)[0].toLowerCase()),
84654
+ at: m.index ?? 0,
84655
+ end: (m.index ?? 0) + m[0].length
84656
+ };
84657
+ }).filter((p) => p.path.length > 1);
84080
84658
  const out = /* @__PURE__ */ new Map();
84081
84659
  for (const { status, at, end } of statuses) {
84082
84660
  const lead = STATUS_TO_PATH.exec(text.slice(end, end + 40));
@@ -84186,11 +84764,12 @@ function dedupeSiteWideSuspectedBugs(drafts) {
84186
84764
  delete plans.suspectedBugs;
84187
84765
  }
84188
84766
  }
84189
- 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;
84767
+ 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;
84190
84768
  var init_survey_bug_signature = __esm({
84191
84769
  "../shared/dist/survey-bug-signature.js"() {
84192
84770
  "use strict";
84193
84771
  init_trailing_slashes();
84772
+ PATH_TRAILING_PUNCTUATION = /[.,;:]/;
84194
84773
  SIGNATURE_SOURCE_MAX_CHARS = 800;
84195
84774
  UUID = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/g;
84196
84775
  PREFIXED_ID = /\b[a-z]{1,8}[-_]\d+\b/g;
@@ -85521,7 +86100,7 @@ function pathIdsIn(text) {
85521
86100
  function findCounterIdPins(code) {
85522
86101
  if (code.length > CLOCK_MAX_CODE_CHARS)
85523
86102
  return [];
85524
- const clean2 = stripLineComments(blankBlockComments3(code));
86103
+ const clean3 = stripLineComments(blankBlockComments3(code));
85525
86104
  const fillValues = [];
85526
86105
  const candidates = [];
85527
86106
  let seenWrite = false;
@@ -85529,24 +86108,24 @@ function findCounterIdPins(code) {
85529
86108
  CALL_RE.lastIndex = 0;
85530
86109
  let m;
85531
86110
  let calls = 0;
85532
- while (calls++ < MAX_CALLS2 && (m = CALL_RE.exec(clean2)) !== null) {
86111
+ while (calls++ < MAX_CALLS2 && (m = CALL_RE.exec(clean3)) !== null) {
85533
86112
  const name = m[1];
85534
86113
  const open = m.index + m[0].length - 1;
85535
86114
  if (VALUE_WRITES.has(name)) {
85536
86115
  seenWrite = true;
85537
- const args2 = argsAt(clean2, open);
86116
+ const args2 = argsAt(clean3, open);
85538
86117
  if (args2 !== null)
85539
86118
  fillValues.push(...staticStrings(args2));
85540
86119
  continue;
85541
86120
  }
85542
86121
  if (createIndex === -1) {
85543
- if (name === "click" && seenWrite && isCreateClick(clean2, m.index))
86122
+ if (name === "click" && seenWrite && isCreateClick(clean3, m.index))
85544
86123
  createIndex = m.index;
85545
86124
  continue;
85546
86125
  }
85547
86126
  if (name === "click")
85548
86127
  continue;
85549
- const args = argsAt(clean2, open);
86128
+ const args = argsAt(clean3, open);
85550
86129
  if (args === null)
85551
86130
  continue;
85552
86131
  const isUrl = URL_SINKS.has(name);
@@ -85565,7 +86144,7 @@ function findCounterIdPins(code) {
85565
86144
  }
85566
86145
  if (createIndex === -1)
85567
86146
  return [];
85568
- const before = clean2.slice(0, createIndex);
86147
+ const before = clean3.slice(0, createIndex);
85569
86148
  let pathIdsBefore = null;
85570
86149
  const findings = [];
85571
86150
  const seen = /* @__PURE__ */ new Set();
@@ -85589,7 +86168,7 @@ function findCounterIdPins(code) {
85589
86168
  continue;
85590
86169
  }
85591
86170
  }
85592
- findings.push({ kind: c.kind, literal: c.literal, line: lineAt(clean2, c.index) });
86171
+ findings.push({ kind: c.kind, literal: c.literal, line: lineAt(clean3, c.index) });
85593
86172
  }
85594
86173
  return findings;
85595
86174
  }
@@ -85884,7 +86463,7 @@ var init_suspicious_expectation = __esm({
85884
86463
  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;
85885
86464
  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;
85886
86465
  STUCK_VETO_RE = new RegExp(`${NOT_STUCK_RE.source}|${LISTS_STUCK_RE.source}`, "i");
85887
- 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;
86466
+ 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;
85888
86467
  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)";
85889
86468
  ELLIPSIS = "(?:\\.{3}|\u2026)";
85890
86469
  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;
@@ -86040,7 +86619,7 @@ function outcomeNamedByAssertion(outcome, code) {
86040
86619
  function namedByStatements(outcome, statements) {
86041
86620
  const text = outcomeText(outcome);
86042
86621
  if (outcome.type === "url") {
86043
- const routes = (text.match(ROUTE_TERM) ?? []).map((r) => r.replace(/[.,;:!?)]+$/, "")).filter((r) => r.length > 1);
86622
+ const routes = (text.match(ROUTE_TERM) ?? []).map((r) => stripTrailingChars(r, ROUTE_TRAILING_PUNCTUATION)).filter((r) => r.length > 1);
86044
86623
  return statements.some((stmt) => {
86045
86624
  if (!/\.(?:waitForURL|toHaveURL)\s{0,20}\(/.test(stmt))
86046
86625
  return false;
@@ -86212,7 +86791,7 @@ function evaluatePromotionGate(input, checks = PROMOTION_QUALITY_CHECKS) {
86212
86791
  ...suspiciousTagged ? { suspiciousPass: { mode: suspicious, covered: suspiciousCovered, wouldHold: suspiciousHold } } : {}
86213
86792
  };
86214
86793
  }
86215
- 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;
86794
+ 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;
86216
86795
  var init_promotion_quality_gate = __esm({
86217
86796
  "../shared/dist/promotion-quality-gate.js"() {
86218
86797
  "use strict";
@@ -86227,11 +86806,12 @@ var init_promotion_quality_gate = __esm({
86227
86806
  init_run_classification();
86228
86807
  init_expected_outcome_coverage();
86229
86808
  init_expect_call();
86809
+ init_trailing_slashes();
86230
86810
  SOURCE_RANK = { generator: 0, classifier: 0, healer: 1, human: 2 };
86231
86811
  strengthCheck = {
86232
86812
  id: "assertion-strength",
86233
- run({ code, tags, strengthGate, flagContradictedAdditions }) {
86234
- const verdict = validateAssertionStrength(code, { flagContradictedAdditions: flagContradictedAdditions === true });
86813
+ run({ code, tags, strengthGate, flagContradictedAdditions, lastActionProof }) {
86814
+ const verdict = validateAssertionStrength(code, { flagContradictedAdditions: flagContradictedAdditions === true, lastActionProof });
86235
86815
  if (verdict.severity === "route-bug")
86236
86816
  return [{ check: "route-bug" }];
86237
86817
  const authDowngrade = verdict.severity === "downgrade" && tags.some((t) => t.toLowerCase() === "@auth");
@@ -86249,7 +86829,7 @@ var init_promotion_quality_gate = __esm({
86249
86829
  MAX_STATEMENTS = 500;
86250
86830
  MAX_STATEMENT_CHARS = 400;
86251
86831
  LITERAL_OR_IDENTIFIER = /'(?:[^'\\\n]|\\.){0,500}'|"(?:[^"\\\n]|\\.){0,500}"|`(?:[^`\\]|\\.){0,2000}`|(?<![\w$.])[A-Za-z_$][\w$]{0,60}/g;
86252
- 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}\(/;
86832
+ 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}\(/;
86253
86833
  FEEDBACK_REGION = /getByRole\s{0,20}\(\s{0,20}['"`](?:alert|status)['"`]|\baria-live\b|\btoast|\bsnackbar\b|\bflash.?message\b/i;
86254
86834
  ONE_SHOT_MESSAGE_REASON = "one-shot success/error banner text";
86255
86835
  FEEDBACK_REGION_ALL = /getByRole\s{0,20}\(\s{0,20}['"`](?:alert|status)['"`]|\baria-live\b|\btoast|\bsnackbar\b|\bflash.?message\b/gi;
@@ -86308,6 +86888,7 @@ var init_promotion_quality_gate = __esm({
86308
86888
  "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}\)/
86309
86889
  };
86310
86890
  ROUTE_TERM = /\/[A-Za-z0-9_./?&=#-]{1,200}/g;
86891
+ ROUTE_TRAILING_PUNCTUATION = /[.,;:!?)]/;
86311
86892
  QUOTED_LITERAL = /'([^'\\\n]{3,200})'|"([^"\\\n]{3,200})"|`([^`\\$\n]{3,200})`/g;
86312
86893
  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;
86313
86894
  MAX_OUTCOME_STATEMENTS = 300;
@@ -86427,8 +87008,8 @@ ${goalText}`);
86427
87008
  return count3 > 0 ? [{ check: "locator-fragment", detail: String(count3), soft: true }] : [];
86428
87009
  }
86429
87010
  };
86430
- 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;
86431
- NEXT_ACTION = /\.(?:goto|reload|click|dblclick|tap|fill|type|pressSequentially|press|check|uncheck|selectOption|setInputFiles|dragTo|clear)\s*\(/;
87011
+ 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;
87012
+ NEXT_ACTION = /\.(?:goto|reload|click|dblclick|tap|fill|type|pressSequentially|press|check|uncheck|setChecked|selectOption|setInputFiles|dragTo|dragAndDrop|clear)\s*\(/;
86432
87013
  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;
86433
87014
  MAX_FILTER_LINES = 2e3;
86434
87015
  MAX_FILTER_LINE_CHARS = 600;
@@ -86543,7 +87124,7 @@ ${goalText}`);
86543
87124
  });
86544
87125
 
86545
87126
  // ../shared/dist/regex-alternation.js
86546
- function closingParens(source) {
87127
+ function closingParens2(source) {
86547
87128
  const close = new Int32Array(source.length).fill(-1);
86548
87129
  const open = [];
86549
87130
  let inClass = false;
@@ -86568,7 +87149,7 @@ function closingParens(source) {
86568
87149
  return close;
86569
87150
  }
86570
87151
  function alternatesAtTop(source) {
86571
- const close = closingParens(source);
87152
+ const close = closingParens2(source);
86572
87153
  let start = source.startsWith("^") ? 1 : 0;
86573
87154
  let end = source.endsWith("$") ? source.length - 1 : source.length;
86574
87155
  while (start < end && source[start] === "(" && close[start] === end - 1) {
@@ -86617,6 +87198,124 @@ var init_page_evidence = __esm({
86617
87198
  }
86618
87199
  });
86619
87200
 
87201
+ // ../shared/dist/stated-limits.js
87202
+ function validDate(v) {
87203
+ const [y, m, d] = v.split("-").map(Number);
87204
+ const t = new Date(Date.UTC(y, m - 1, d));
87205
+ return t.getUTCFullYear() === y && t.getUTCMonth() === m - 1 && t.getUTCDate() === d;
87206
+ }
87207
+ function isoFor(type, v) {
87208
+ const re = DATE_RE[type];
87209
+ if (!re || !re.test(v))
87210
+ return false;
87211
+ if (type === "date")
87212
+ return validDate(v);
87213
+ if (type === "datetime-local")
87214
+ return validDate(v.slice(0, 10)) && Number(v.slice(11, 13)) < 24 && Number(v.slice(14, 16)) < 60;
87215
+ if (type === "time")
87216
+ return Number(v.slice(0, 2)) < 24 && Number(v.slice(3, 5)) < 60;
87217
+ if (type === "month")
87218
+ return Number(v.slice(5, 7)) >= 1 && Number(v.slice(5, 7)) <= 12;
87219
+ return Number(v.slice(6, 8)) >= 1 && Number(v.slice(6, 8)) <= 53;
87220
+ }
87221
+ function clean2(raw) {
87222
+ return typeof raw === "string" ? raw.trim() : "";
87223
+ }
87224
+ function statedLimitsFromAttrs(attrs, inputType) {
87225
+ const type = (inputType ?? "").trim().toLowerCase();
87226
+ const limits = [];
87227
+ const add = (k, v) => {
87228
+ limits.push({ k, v });
87229
+ };
87230
+ if (NUMBER_TYPES.has(type)) {
87231
+ for (const [attr, k] of [["min", "min"], ["max", "max"]]) {
87232
+ const v = clean2(attrs[attr]);
87233
+ if (NUMBER_RE.test(v))
87234
+ add(k, v);
87235
+ }
87236
+ const step = clean2(attrs.step);
87237
+ if (NUMBER_RE.test(step) && Number(step) > 0)
87238
+ add("step", step);
87239
+ } else if (type in DATE_RE) {
87240
+ for (const [attr, k] of [["min", "min"], ["max", "max"]]) {
87241
+ const v = clean2(attrs[attr]);
87242
+ if (isoFor(type, v))
87243
+ add(k, v);
87244
+ }
87245
+ } else if (TEXT_TYPES.has(type)) {
87246
+ for (const [attr, k] of [["minlength", "minLength"], ["maxlength", "maxLength"]]) {
87247
+ const v = clean2(attrs[attr]);
87248
+ if (LENGTH_RE.test(v))
87249
+ add(k, String(Number(v)));
87250
+ }
87251
+ const pattern = cleanPattern(clean2(attrs.pattern).slice(0, STATED_PATTERN_MAX));
87252
+ if (pattern)
87253
+ add("pattern", pattern);
87254
+ }
87255
+ return limits.length > 0 ? { v: STATED_LIMITS_VERSION, limits } : null;
87256
+ }
87257
+ function cleanPattern(text) {
87258
+ return text.replace(/[\u0000-\u001f\u007f]/g, "").replace(/[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/g, "");
87259
+ }
87260
+ function readStatedLimits(stored) {
87261
+ if (!stored || typeof stored !== "object" || Array.isArray(stored))
87262
+ return [];
87263
+ const rec = stored;
87264
+ if (rec.v !== STATED_LIMITS_VERSION || !Array.isArray(rec.limits))
87265
+ return [];
87266
+ const out = [];
87267
+ const seen = /* @__PURE__ */ new Set();
87268
+ for (const item of rec.limits.slice(0, MAX_LIMITS * 2)) {
87269
+ if (!item || typeof item !== "object")
87270
+ continue;
87271
+ const { k, v } = item;
87272
+ if (typeof k !== "string" || typeof v !== "string" || !KINDS.includes(k) || seen.has(k))
87273
+ continue;
87274
+ if (v.length === 0 || v.length > STATED_PATTERN_MAX)
87275
+ continue;
87276
+ const text = k === "pattern" ? cleanPattern(v) : v;
87277
+ if (text === "")
87278
+ continue;
87279
+ if (k === "minLength" || k === "maxLength") {
87280
+ if (!LENGTH_RE.test(v))
87281
+ continue;
87282
+ } else if (k === "step") {
87283
+ if (!NUMBER_RE.test(v))
87284
+ continue;
87285
+ } else if (k === "min" || k === "max") {
87286
+ if (!NUMBER_RE.test(v) && !Object.values(DATE_RE).some((re) => re.test(v)))
87287
+ continue;
87288
+ }
87289
+ seen.add(k);
87290
+ out.push({ k, v: text });
87291
+ }
87292
+ return out;
87293
+ }
87294
+ function statedLimitsRecord(limits) {
87295
+ return limits.length > 0 ? { v: STATED_LIMITS_VERSION, limits } : null;
87296
+ }
87297
+ var STATED_LIMITS_VERSION, STATED_PATTERN_MAX, KINDS, MAX_LIMITS, TEXT_TYPES, NUMBER_TYPES, DATE_RE, NUMBER_RE, LENGTH_RE;
87298
+ var init_stated_limits = __esm({
87299
+ "../shared/dist/stated-limits.js"() {
87300
+ "use strict";
87301
+ STATED_LIMITS_VERSION = 1;
87302
+ STATED_PATTERN_MAX = 200;
87303
+ KINDS = ["min", "max", "minLength", "maxLength", "step", "pattern"];
87304
+ MAX_LIMITS = KINDS.length;
87305
+ TEXT_TYPES = /* @__PURE__ */ new Set(["text", "search", "email", "url", "tel", "password", "textarea", ""]);
87306
+ NUMBER_TYPES = /* @__PURE__ */ new Set(["number", "range"]);
87307
+ DATE_RE = {
87308
+ date: /^\d{4}-\d{2}-\d{2}$/,
87309
+ "datetime-local": /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?$/,
87310
+ time: /^\d{2}:\d{2}(?::\d{2})?$/,
87311
+ month: /^\d{4}-\d{2}$/,
87312
+ week: /^\d{4}-W\d{2}$/
87313
+ };
87314
+ NUMBER_RE = /^-?\d{1,15}(?:\.\d{1,10})?$/;
87315
+ LENGTH_RE = /^\d{1,7}$/;
87316
+ }
87317
+ });
87318
+
86620
87319
  // ../shared/dist/index.js
86621
87320
  var dist_exports = {};
86622
87321
  __export(dist_exports, {
@@ -86624,9 +87323,11 @@ __export(dist_exports, {
86624
87323
  ACTION_BATCH_MAX_SIZE: () => ACTION_BATCH_MAX_SIZE,
86625
87324
  ADDON_DEFINITIONS: () => ADDON_DEFINITIONS,
86626
87325
  ADMIN_EMAIL_LIMITS: () => ADMIN_EMAIL_LIMITS,
87326
+ ADMIN_FORCE_CANCELLED_RUN_ERROR: () => ADMIN_FORCE_CANCELLED_RUN_ERROR,
86627
87327
  ADMIN_VIEW_DEFAULT_RETURN_TO: () => ADMIN_VIEW_DEFAULT_RETURN_TO,
86628
87328
  AGENT_AI_PROVIDER_PREFIXES: () => AGENT_AI_PROVIDER_PREFIXES,
86629
87329
  AI_CREDIT_MARGIN: () => AI_CREDIT_MARGIN,
87330
+ ALLOWED_PROVIDER_CHAIN_HOPS: () => ALLOWED_PROVIDER_CHAIN_HOPS,
86630
87331
  API_AUTH_HEADERS_ENV_KEY: () => API_AUTH_HEADERS_ENV_KEY,
86631
87332
  API_MODEL_CATALOG: () => API_MODEL_CATALOG,
86632
87333
  API_MODEL_FIELDS: () => API_MODEL_FIELDS,
@@ -86666,12 +87367,14 @@ __export(dist_exports, {
86666
87367
  BUG_TEST_BROKEN_REASON: () => BUG_TEST_BROKEN_REASON,
86667
87368
  BUG_TEST_BROKEN_TAG: () => BUG_TEST_BROKEN_TAG,
86668
87369
  CACHE_PRICE_PER_M: () => CACHE_PRICE_PER_M,
87370
+ CANCELLED_REASON_PREFIXES: () => CANCELLED_REASON_PREFIXES,
86669
87371
  CANVAS_UNSUPPORTED_BODY: () => CANVAS_UNSUPPORTED_BODY,
86670
87372
  CANVAS_UNSUPPORTED_CODE: () => CANVAS_UNSUPPORTED_CODE,
86671
87373
  CANVAS_UNSUPPORTED_NOT_CHARGED: () => CANVAS_UNSUPPORTED_NOT_CHARGED,
86672
87374
  CANVAS_UNSUPPORTED_OVERRIDE_HINT: () => CANVAS_UNSUPPORTED_OVERRIDE_HINT,
86673
87375
  CANVAS_UNSUPPORTED_TITLE: () => CANVAS_UNSUPPORTED_TITLE,
86674
87376
  CAPTURE_JSON_STRUCTURE_LIMITS: () => CAPTURE_JSON_STRUCTURE_LIMITS,
87377
+ CHAIN_CANCELLED_RUN_ERROR: () => CHAIN_CANCELLED_RUN_ERROR,
86675
87378
  CHAT_FAILOVER_KEY: () => CHAT_FAILOVER_KEY,
86676
87379
  CLAUDE_CLI_DEFAULT_EFFORT: () => CLAUDE_CLI_DEFAULT_EFFORT,
86677
87380
  CLAUDE_CLI_DEFAULT_MODEL: () => CLAUDE_CLI_DEFAULT_MODEL,
@@ -86708,6 +87411,7 @@ __export(dist_exports, {
86708
87411
  DEFAULT_GEMINI_SCREEN_MODEL: () => DEFAULT_GEMINI_SCREEN_MODEL,
86709
87412
  DEFAULT_JSON_STRUCTURE_LIMITS: () => DEFAULT_JSON_STRUCTURE_LIMITS,
86710
87413
  DEFAULT_PLATFORM_DB_POOL_SIZE: () => DEFAULT_PLATFORM_DB_POOL_SIZE,
87414
+ DEFAULT_PROVIDER_ACCOUNT_ID: () => DEFAULT_PROVIDER_ACCOUNT_ID,
86711
87415
  DEFAULT_SHARED_DB_POOL_SIZE: () => DEFAULT_SHARED_DB_POOL_SIZE,
86712
87416
  DEFAULT_STT_MODEL: () => DEFAULT_STT_MODEL,
86713
87417
  DEFAULT_STT_MODEL_BY_PROVIDER: () => DEFAULT_STT_MODEL_BY_PROVIDER,
@@ -86724,6 +87428,7 @@ __export(dist_exports, {
86724
87428
  ENVIRONMENTAL_ERROR_STORED_PREFIXES: () => ENVIRONMENTAL_ERROR_STORED_PREFIXES,
86725
87429
  EVIDENCE_NOTE_PREFIXES: () => EVIDENCE_NOTE_PREFIXES,
86726
87430
  EXPECT_CALL_RE: () => EXPECT_CALL_RE,
87431
+ FEATURE_ROW_RUN_WINDOW: () => FEATURE_ROW_RUN_WINDOW,
86727
87432
  FLOW_PHASE_LABELS: () => FLOW_PHASE_LABELS,
86728
87433
  FLOW_PHASE_TYPES: () => FLOW_PHASE_TYPES,
86729
87434
  FLOW_SERVER_TYPES: () => FLOW_SERVER_TYPES,
@@ -86791,6 +87496,8 @@ __export(dist_exports, {
86791
87496
  MAX_ORG_NAME_LENGTH: () => MAX_ORG_NAME_LENGTH,
86792
87497
  MAX_PASSWORD_BYTES: () => MAX_PASSWORD_BYTES,
86793
87498
  MAX_PLAYWRIGHT_CODE_LENGTH: () => MAX_PLAYWRIGHT_CODE_LENGTH,
87499
+ MAX_PROVIDER_ACCOUNTS: () => MAX_PROVIDER_ACCOUNTS,
87500
+ MAX_PROVIDER_ACCOUNT_MODELS: () => MAX_PROVIDER_ACCOUNT_MODELS,
86794
87501
  MAX_SURVEY_ENTRY_URLS: () => MAX_SURVEY_ENTRY_URLS,
86795
87502
  MAX_SURVEY_ENTRY_URL_LENGTH: () => MAX_SURVEY_ENTRY_URL_LENGTH,
86796
87503
  MCP_ARMY_LIMITS: () => MCP_ARMY_LIMITS,
@@ -86826,7 +87533,7 @@ __export(dist_exports, {
86826
87533
  MUSE_ENGINE_DEFAULT_MODEL: () => MUSE_ENGINE_DEFAULT_MODEL,
86827
87534
  MUSE_ENGINE_MODELS: () => MUSE_ENGINE_MODELS,
86828
87535
  NOREPLY_EMAIL: () => NOREPLY_EMAIL,
86829
- OPENCODE_GO_2_PREFIX: () => OPENCODE_GO_2_PREFIX,
87536
+ OPENCODE_GO_DEEPSEEK_GLM_MODELS: () => OPENCODE_GO_DEEPSEEK_GLM_MODELS,
86830
87537
  OPENCODE_GO_MODEL_PATTERN: () => OPENCODE_GO_MODEL_PATTERN,
86831
87538
  OPENCODE_GO_PREFIX: () => OPENCODE_GO_PREFIX,
86832
87539
  OPENROUTER_MAX_CALL_COST_USD: () => OPENROUTER_MAX_CALL_COST_USD,
@@ -86846,10 +87553,12 @@ __export(dist_exports, {
86846
87553
  PLAN_FEATURES: () => PLAN_FEATURES,
86847
87554
  PLAN_FEATURE_LOCKED_CODE: () => PLAN_FEATURE_LOCKED_CODE,
86848
87555
  PLAN_ORDER: () => PLAN_ORDER,
87556
+ PLATFORM_ADMIN_CANCELLED_RUN_ERROR: () => PLATFORM_ADMIN_CANCELLED_RUN_ERROR,
86849
87557
  PLATFORM_ADMIN_EMAIL: () => PLATFORM_ADMIN_EMAIL,
86850
87558
  PLATFORM_DEFAULT_PROVIDER: () => PLATFORM_DEFAULT_PROVIDER,
86851
87559
  PLATFORM_PLAN_BILLING: () => PLATFORM_PLAN_BILLING,
86852
87560
  PLATFORM_SERVER_APPLICATION_NAME: () => PLATFORM_SERVER_APPLICATION_NAME,
87561
+ PLATFORM_SUPPORT_CANCELLED_RUN_ERROR: () => PLATFORM_SUPPORT_CANCELLED_RUN_ERROR,
86853
87562
  POST_SPECIAL_TRIAL_DAYS: () => POST_SPECIAL_TRIAL_DAYS,
86854
87563
  PRESET_DIRECT_FALLBACK: () => PRESET_DIRECT_FALLBACK,
86855
87564
  PRODUCTION_READ_ONLY_GUARD_TEMPLATE: () => PRODUCTION_READ_ONLY_GUARD_TEMPLATE,
@@ -86861,8 +87570,12 @@ __export(dist_exports, {
86861
87570
  PROJECT_OVERRIDE_FIELDS: () => PROJECT_OVERRIDE_FIELDS,
86862
87571
  PROMOTION_GATE_MAX_CODE_CHARS: () => PROMOTION_GATE_MAX_CODE_CHARS,
86863
87572
  PROMOTION_QUALITY_CHECKS: () => PROMOTION_QUALITY_CHECKS,
87573
+ PROVIDER_ACCOUNT_ID_PATTERN: () => PROVIDER_ACCOUNT_ID_PATTERN,
87574
+ PROVIDER_ACCOUNT_MODEL_PATTERN: () => PROVIDER_ACCOUNT_MODEL_PATTERN,
86864
87575
  PROVIDER_CHAINS: () => PROVIDER_CHAINS,
87576
+ PROVIDER_CHAINS_SETTING_MAX_CHARS: () => PROVIDER_CHAINS_SETTING_MAX_CHARS,
86865
87577
  PROVIDER_CHAIN_LEADS: () => PROVIDER_CHAIN_LEADS,
87578
+ PROVIDER_CHAIN_MAX_ENTRIES: () => PROVIDER_CHAIN_MAX_ENTRIES,
86866
87579
  PROVIDER_HOST_NOT_OFFERED: () => PROVIDER_HOST_NOT_OFFERED,
86867
87580
  PUBLIC_PLANS: () => PUBLIC_PLANS,
86868
87581
  QUARANTINE_CLOSE_TAGS: () => QUARANTINE_CLOSE_TAGS,
@@ -86870,10 +87583,12 @@ __export(dist_exports, {
86870
87583
  READ_ONLY_TAGS: () => READ_ONLY_TAGS,
86871
87584
  RECHECKED_NOTES: () => RECHECKED_NOTES,
86872
87585
  RECORDING_VALUE_PLACEHOLDERS: () => RECORDING_VALUE_PLACEHOLDERS,
87586
+ RENDER_IDENTITY_MAX_LENGTH: () => RENDER_IDENTITY_MAX_LENGTH,
86873
87587
  REPOSITORY_CLIS: () => REPOSITORY_CLIS,
86874
87588
  RESERVED_PROJECT_KEYS: () => RESERVED_PROJECT_KEYS,
86875
87589
  RESULT_STRUCTURE_TOO_COSTLY_CODE: () => RESULT_STRUCTURE_TOO_COSTLY_CODE,
86876
87590
  RETRIED_ATTEMPTS_KEY: () => RETRIED_ATTEMPTS_KEY,
87591
+ RETRY_HELD_RUN_ERROR: () => RETRY_HELD_RUN_ERROR,
86877
87592
  RUNNER_INTERRUPT_ERROR_PREFIX: () => RUNNER_INTERRUPT_ERROR_PREFIX,
86878
87593
  RUN_INTERRUPTED_ERROR_PREFIXES: () => RUN_INTERRUPTED_ERROR_PREFIXES,
86879
87594
  RUN_INTERRUPTION_NOT_RETRIED_MARKERS: () => RUN_INTERRUPTION_NOT_RETRIED_MARKERS,
@@ -86886,6 +87601,8 @@ __export(dist_exports, {
86886
87601
  SHARED_SERVER_APPLICATION_NAME: () => SHARED_SERVER_APPLICATION_NAME,
86887
87602
  SPEND_SOURCE_KEY: () => SPEND_SOURCE_KEY,
86888
87603
  SPEND_SOURCE_SERVER: () => SPEND_SOURCE_SERVER,
87604
+ STATED_LIMITS_VERSION: () => STATED_LIMITS_VERSION,
87605
+ STATED_PATTERN_MAX: () => STATED_PATTERN_MAX,
86889
87606
  STATIC_STANDARD_MIN_INTERACTIVE: () => STATIC_STANDARD_MIN_INTERACTIVE,
86890
87607
  STT_MODEL_OPTIONS: () => STT_MODEL_OPTIONS,
86891
87608
  SUCCESS_NOTICE_TEXT: () => SUCCESS_NOTICE_TEXT,
@@ -86915,14 +87632,20 @@ __export(dist_exports, {
86915
87632
  UI_VARIANTS: () => UI_VARIANTS,
86916
87633
  UNEXPECTED_PASS_ERROR_PREFIX: () => UNEXPECTED_PASS_ERROR_PREFIX,
86917
87634
  USER_CANCELLED_RUN_ERROR: () => USER_CANCELLED_RUN_ERROR,
87635
+ VISUAL_MASK_SELECTORS_MAX: () => VISUAL_MASK_SELECTORS_MAX,
87636
+ VISUAL_MASK_SELECTOR_FORBIDDEN: () => VISUAL_MASK_SELECTOR_FORBIDDEN,
87637
+ VISUAL_MASK_SELECTOR_MAX_LENGTH: () => VISUAL_MASK_SELECTOR_MAX_LENGTH,
87638
+ VISUAL_V1_IDENTITY: () => VISUAL_V1_IDENTITY,
86918
87639
  VOICE_PREVIEW_SAMPLE: () => VOICE_PREVIEW_SAMPLE,
86919
87640
  WEB_PROXY_CHAIN_HEADER: () => WEB_PROXY_CHAIN_HEADER,
86920
87641
  WITHHELD_CREDENTIAL_TEXT: () => WITHHELD_CREDENTIAL_TEXT,
86921
87642
  ZAI_MODEL_PATTERN: () => ZAI_MODEL_PATTERN,
86922
87643
  ZAI_PREFIX: () => ZAI_PREFIX,
87644
+ accountServesModel: () => accountServesModel,
86923
87645
  accumulateStatements: () => accumulateStatements,
86924
87646
  adoptBrokerFailover: () => adoptBrokerFailover,
86925
87647
  aiCostToCreditCents: () => aiCostToCreditCents,
87648
+ allowedProviderChainHosts: () => allowedProviderChainHosts,
86926
87649
  alternatesAtTop: () => alternatesAtTop,
86927
87650
  analyzeDerivedValues: () => analyzeDerivedValues,
86928
87651
  analyzeMobileSelectorQuality: () => analyzeMobileSelectorQuality,
@@ -86935,6 +87658,7 @@ __export(dist_exports, {
86935
87658
  analyzeUnreachedUrlWaits: () => analyzeUnreachedUrlWaits,
86936
87659
  analyzeUrlWaits: () => analyzeUrlWaits,
86937
87660
  analyzeWriteResponse: () => analyzeWriteResponse,
87661
+ androidResourceOwners: () => androidResourceOwners,
86938
87662
  apiModelCardsFromConfig: () => apiModelCardsFromConfig,
86939
87663
  apiModelsForProvider: () => apiModelsForProvider,
86940
87664
  apiProviderModelLabel: () => apiProviderModelLabel,
@@ -86945,6 +87669,7 @@ __export(dist_exports, {
86945
87669
  applyLaunchSpecialPlan: () => applyLaunchSpecialPlan,
86946
87670
  asPlainExpect: () => asPlainExpect,
86947
87671
  assertionOnlyCode: () => assertionOnlyCode,
87672
+ assertionStrengthHoistedReadsOn: () => assertionStrengthHoistedReadsOn,
86948
87673
  assertionsReadVisibleContent: () => assertionsReadVisibleContent,
86949
87674
  assessCodeOutcomeCoverage: () => assessCodeOutcomeCoverage,
86950
87675
  assessTitleCoverage: () => assessTitleCoverage,
@@ -87028,6 +87753,7 @@ __export(dist_exports, {
87028
87753
  deepgramSpeak: () => deepgramSpeak,
87029
87754
  deepgramTranscribe: () => deepgramTranscribe,
87030
87755
  defaultConfirmModel: () => defaultConfirmModel,
87756
+ defaultProviderChainEntries: () => defaultProviderChainEntries,
87031
87757
  defaultScreenModel: () => defaultScreenModel,
87032
87758
  defaultVoiceForTtsModel: () => defaultVoiceForTtsModel,
87033
87759
  deriveAuthPlan: () => deriveAuthPlan,
@@ -87070,6 +87796,7 @@ __export(dist_exports, {
87070
87796
  extractOutcomeSearchTerms: () => extractOutcomeSearchTerms,
87071
87797
  featureForAiCallType: () => featureForAiCallType,
87072
87798
  featureForMcpTool: () => featureForMcpTool,
87799
+ featureRowRunStatus: () => featureRowRunStatus,
87073
87800
  findBracedClause: () => findBracedClause,
87074
87801
  findCounterIdPins: () => findCounterIdPins,
87075
87802
  findCredentialTextLeftover: () => findCredentialTextLeftover,
@@ -87118,6 +87845,7 @@ __export(dist_exports, {
87118
87845
  getPlanChangeType: () => getPlanChangeType,
87119
87846
  getPlanDefinition: () => getPlanDefinition,
87120
87847
  getPlanLabel: () => getPlanLabel,
87848
+ getProviderChainOverrides: () => getProviderChainOverrides,
87121
87849
  getTrialDaysRemaining: () => getTrialDaysRemaining,
87122
87850
  googleBrokerNavigationUrl: () => googleBrokerNavigationUrl,
87123
87851
  googleBrokerUnavailableLine: () => googleBrokerUnavailableLine,
@@ -87131,10 +87859,10 @@ __export(dist_exports, {
87131
87859
  hasEncodedRequestBody: () => hasEncodedRequestBody,
87132
87860
  hasEngineSpecParts: () => hasEngineSpecParts,
87133
87861
  hasOnlyConstantAssertions: () => hasOnlyConstantAssertions,
87134
- hasOpenCodeGo2Prefix: () => hasOpenCodeGo2Prefix,
87135
87862
  hasOpenCodeGoPrefix: () => hasOpenCodeGoPrefix,
87136
87863
  hasOpenRouterPrefix: () => hasOpenRouterPrefix,
87137
87864
  hasPlatformLocator: () => hasPlatformLocator,
87865
+ hasProviderChainLeadsByDefault: () => hasProviderChainLeadsByDefault,
87138
87866
  hasReadOnlyMarker: () => hasReadOnlyMarker,
87139
87867
  hasSurveyNotes: () => hasSurveyNotes,
87140
87868
  hasTestFailContract: () => hasTestFailContract,
@@ -87160,8 +87888,11 @@ __export(dist_exports, {
87160
87888
  isAuthTokenName: () => isAuthTokenName,
87161
87889
  isBlockedEnvVar: () => isBlockedEnvVar,
87162
87890
  isBrowserCliEngine: () => isBrowserCliEngine,
87891
+ isBrowserVersion: () => isBrowserVersion,
87163
87892
  isBugTestBrokenError: () => isBugTestBrokenError,
87893
+ isCancelledReason: () => isCancelledReason,
87164
87894
  isCanvasUnsupportedMessage: () => isCanvasUnsupportedMessage,
87895
+ isChainablePrimary: () => isChainablePrimary,
87165
87896
  isCliEngineModelId: () => isCliEngineModelId,
87166
87897
  isCliEngineSpec: () => isCliEngineSpec,
87167
87898
  isCodeFactNote: () => isCodeFactNote,
@@ -87185,6 +87916,7 @@ __export(dist_exports, {
87185
87916
  isIdentityMobileXpath: () => isIdentityMobileXpath,
87186
87917
  isIdentityProviderSurface: () => isIdentityProviderSurface,
87187
87918
  isIncidentalAssetPath: () => isIncidentalAssetPath,
87919
+ isIntentionalNegativeAuthTest: () => isIntentionalNegativeAuthTest,
87188
87920
  isIpOrLocalhost: () => isIpOrLocalhost,
87189
87921
  isKnownPageType: () => isKnownPageType,
87190
87922
  isLaunchSpecialActive: () => isLaunchSpecialActive,
@@ -87212,6 +87944,7 @@ __export(dist_exports, {
87212
87944
  isRunInterruptedError: () => isRunInterruptedError,
87213
87945
  isRunnableStep: () => isRunnableStep,
87214
87946
  isSafeDeepLink: () => isSafeDeepLink,
87947
+ isSafeMaskSelector: () => isSafeMaskSelector,
87215
87948
  isSameRegistrableDomainHost: () => isSameRegistrableDomainHost,
87216
87949
  isSelectableApiModel: () => isSelectableApiModel,
87217
87950
  isSelfServePlan: () => isSelfServePlan,
@@ -87275,6 +88008,7 @@ __export(dist_exports, {
87275
88008
  normalizeFinishReason: () => normalizeFinishReason,
87276
88009
  normalizeNoteText: () => normalizeNoteText,
87277
88010
  normalizePageType: () => normalizePageType,
88011
+ normalizeProviderAccountModels: () => normalizeProviderAccountModels,
87278
88012
  normalizeTtsModel: () => normalizeTtsModel,
87279
88013
  normalizeTtsVoiceId: () => normalizeTtsVoiceId,
87280
88014
  noteChatFailover: () => noteChatFailover,
@@ -87299,13 +88033,18 @@ __export(dist_exports, {
87299
88033
  parseGeminiModelsPayload: () => parseGeminiModelsPayload,
87300
88034
  parseGoogleAuthorizeUrl: () => parseGoogleAuthorizeUrl,
87301
88035
  parseGoogleGisRequestUrl: () => parseGoogleGisRequestUrl,
88036
+ parseLastActionProofMode: () => parseLastActionProofMode,
87302
88037
  parseLimitParam: () => parseLimitParam,
87303
88038
  parseOffsetParam: () => parseOffsetParam,
87304
88039
  parsePageParam: () => parsePageParam,
87305
88040
  parsePgIntSetting: () => parsePgIntSetting,
87306
88041
  parseProjectEngineOverrides: () => parseProjectEngineOverrides,
87307
88042
  parsePromotionGateMode: () => parsePromotionGateMode,
88043
+ parseProviderAccountLists: () => parseProviderAccountLists,
88044
+ parseProviderChainOverrides: () => parseProviderChainOverrides,
87308
88045
  parseRecheckClearMode: () => parseRecheckClearMode,
88046
+ parseRenderIdentity: () => parseRenderIdentity,
88047
+ parseStoredProviderChainOverrides: () => parseStoredProviderChainOverrides,
87309
88048
  parseSurveyEntryUrls: () => parseSurveyEntryUrls,
87310
88049
  parseSuspiciousPassMode: () => parseSuspiciousPassMode,
87311
88050
  parseTaskModelUpdate: () => parseTaskModelUpdate,
@@ -87336,7 +88075,9 @@ __export(dist_exports, {
87336
88075
  projectAppHosts: () => projectAppHosts,
87337
88076
  providerChainFor: () => providerChainFor,
87338
88077
  providerChainHostOf: () => providerChainHostOf,
88078
+ providerChainHostProvider: () => providerChainHostProvider,
87339
88079
  providerChainLeadsFor: () => providerChainLeadsFor,
88080
+ providerChainModelKey: () => providerChainModelKey,
87340
88081
  providerChainPrimariesForHop: () => providerChainPrimariesForHop,
87341
88082
  providerHostPlan: () => providerHostPlan,
87342
88083
  readBalanced: () => readBalanced,
@@ -87346,6 +88087,7 @@ __export(dist_exports, {
87346
88087
  readEnabledEngines: () => readEnabledEngines,
87347
88088
  readProjectEngineOverrides: () => readProjectEngineOverrides,
87348
88089
  readRetriedAttempts: () => readRetriedAttempts,
88090
+ readStatedLimits: () => readStatedLimits,
87349
88091
  readStaticPageCanvasSignals: () => readStaticPageCanvasSignals,
87350
88092
  readSurveyEntryUrls: () => readSurveyEntryUrls,
87351
88093
  readSurveyNotes: () => readSurveyNotes,
@@ -87358,6 +88100,7 @@ __export(dist_exports, {
87358
88100
  regexLiteralEnd: () => regexLiteralEnd2,
87359
88101
  regexOpensAfter: () => regexOpensAfter,
87360
88102
  registrableDomain: () => registrableDomain,
88103
+ renderIdentity: () => renderIdentity,
87361
88104
  renderSurveyNotesForPrompt: () => renderSurveyNotesForPrompt,
87362
88105
  repairPageType: () => repairPageType,
87363
88106
  replaceBlockComments: () => replaceBlockComments,
@@ -87405,11 +88148,14 @@ __export(dist_exports, {
87405
88148
  selectorCandidatesForStep: () => selectorCandidatesForStep,
87406
88149
  selectorForValue: () => selectorForValue,
87407
88150
  setAgenticCliRuntime: () => setAgenticCliRuntime,
88151
+ setProviderChainOverrides: () => setProviderChainOverrides,
87408
88152
  signupPlanFor: () => signupPlanFor,
87409
88153
  simplifyMobileWanderingInPlace: () => simplifyMobileWanderingInPlace,
87410
88154
  siteOf: () => siteOf,
87411
88155
  specItemsFromTestPlans: () => specItemsFromTestPlans,
87412
88156
  splitAnswerLines: () => splitAnswerLines,
88157
+ statedLimitsFromAttrs: () => statedLimitsFromAttrs,
88158
+ statedLimitsRecord: () => statedLimitsRecord,
87413
88159
  stepAppliesToPlatform: () => stepAppliesToPlatform,
87414
88160
  stepConsoleLines: () => stepConsoleLines,
87415
88161
  stepIsDivergentForPlatform: () => stepIsDivergentForPlatform,
@@ -87421,7 +88167,10 @@ __export(dist_exports, {
87421
88167
  stripCodeFactPrefix: () => stripCodeFactPrefix,
87422
88168
  stripCoverageComments: () => stripCoverageComments,
87423
88169
  stripEnvironmentalPrefix: () => stripEnvironmentalPrefix,
88170
+ stripFractionZeros: () => stripFractionZeros,
88171
+ stripLineComment: () => stripLineComment,
87424
88172
  stripLoginScaffolding: () => stripLoginScaffolding,
88173
+ stripTrailingChars: () => stripTrailingChars,
87425
88174
  stripTrailingSlashes: () => stripTrailingSlashes,
87426
88175
  strongerNeedsReviewSource: () => strongerNeedsReviewSource,
87427
88176
  sttModelsForProvider: () => sttModelsForProvider,
@@ -87439,10 +88188,14 @@ __export(dist_exports, {
87439
88188
  ttsFallbackModel: () => ttsFallbackModel,
87440
88189
  ttsProviderForModel: () => ttsProviderForModel,
87441
88190
  unexpectedPassError: () => unexpectedPassError,
88191
+ v1RenderIdentity: () => v1RenderIdentity,
87442
88192
  validateAppMapDocument: () => validateAppMapDocument,
87443
88193
  validateAssertionStrength: () => validateAssertionStrength,
87444
88194
  validateEngineSpec: () => validateEngineSpec,
88195
+ validateProviderChainEntries: () => validateProviderChainEntries,
88196
+ validateProviderChainsSetting: () => validateProviderChainsSetting,
87445
88197
  validateTaskModelId: () => validateTaskModelId,
88198
+ visualBaselinesEnabled: () => visualBaselinesEnabled,
87446
88199
  visualProDailyBudget: () => visualProDailyBudget,
87447
88200
  voiceMatchesTtsModel: () => voiceMatchesTtsModel,
87448
88201
  voicesForTtsModel: () => voicesForTtsModel,
@@ -87505,11 +88258,14 @@ var init_dist = __esm({
87505
88258
  init_deepgram_voices();
87506
88259
  init_feature_flags();
87507
88260
  init_env_flags();
88261
+ init_visual_compare_limits();
88262
+ init_visual_baseline_identity();
87508
88263
  init_analytics_url();
87509
88264
  init_agentic_cli_runtime();
87510
88265
  init_api_model_catalog();
87511
88266
  init_engine_spec();
87512
88267
  init_provider_chains();
88268
+ init_provider_accounts();
87513
88269
  init_project_engine_overrides();
87514
88270
  init_ai_profiles();
87515
88271
  init_openrouter_usage();
@@ -87551,6 +88307,297 @@ var init_dist = __esm({
87551
88307
  init_regex_alternation();
87552
88308
  init_page_evidence();
87553
88309
  init_trailing_slashes();
88310
+ init_stated_limits();
88311
+ }
88312
+ });
88313
+
88314
+ // ../runner-core/dist/services/harness-init-script.js
88315
+ var require_harness_init_script = __commonJS({
88316
+ "../runner-core/dist/services/harness-init-script.js"(exports2) {
88317
+ "use strict";
88318
+ Object.defineProperty(exports2, "__esModule", { value: true });
88319
+ exports2.HARNESS_INIT_SOURCE_URL = void 0;
88320
+ exports2.withHarnessSourceUrl = withHarnessSourceUrl;
88321
+ exports2.buildAnimationDisablingSource = buildAnimationDisablingSource;
88322
+ exports2.isHarnessInitLocation = isHarnessInitLocation;
88323
+ exports2.isHarnessInitErrorStack = isHarnessInitErrorStack;
88324
+ exports2.isHarnessInitConsoleLine = isHarnessInitConsoleLine;
88325
+ exports2.stripHarnessConsoleEntries = stripHarnessConsoleEntries;
88326
+ var node_crypto_1 = require("crypto");
88327
+ var shared_1 = (init_dist(), __toCommonJS(dist_exports));
88328
+ exports2.HARNESS_INIT_SOURCE_URL = `vqa-harness-init-${(0, node_crypto_1.randomBytes)(4).toString("hex")}.js`;
88329
+ var TRANSITION_LOOP_LIMIT = 8;
88330
+ var TRANSITION_LOOP_WINDOW_MS = 1e3;
88331
+ function withHarnessSourceUrl(content) {
88332
+ return `${content}
88333
+ //# sourceURL=${exports2.HARNESS_INIT_SOURCE_URL}`;
88334
+ }
88335
+ function buildAnimationDisablingSource(opts) {
88336
+ const finishTransitions = opts.finishTransitions ?? (0, shared_1.parseBoolFlag)(process.env.HARNESS_FINISH_CSS_TRANSITIONS);
88337
+ const css = finishTransitions ? "*, *::before, *::after { animation-duration: 0s !important; animation-delay: 0s !important; }" : "*, *::before, *::after { transition-duration: 0s !important; animation-duration: 0s !important; transition-delay: 0s !important; animation-delay: 0s !important; }";
88338
+ return withHarnessSourceUrl(`(() => {
88339
+ // An error the browser throws for the page's own value, passed through one of the wrappers below (a bad
88340
+ // adoptedStyleSheets list, animate timing it refuses), is the app's: stock Playwright reports it as a page error.
88341
+ // Thrown from our wrapper, its top frame would be this script, and the console evidence drops such an error as
88342
+ // the runner's own. Our frames come out of its stack, so the app's call is the top frame, as without the harness.
88343
+ // (Under Error.stackTraceLimit the stack then has one or two frames fewer than stock's; the top frame is right.)
88344
+ const own = ${JSON.stringify(exports2.HARNESS_INIT_SOURCE_URL)};
88345
+ const appError = (e) => {
88346
+ try {
88347
+ const stack = e !== null && typeof e === 'object' ? e.stack : undefined;
88348
+ if (typeof stack === 'string' && stack.includes(own)) e.stack = stack.split('\\n').filter((line) => !line.includes(own)).join('\\n');
88349
+ } catch (x) {}
88350
+ return e;
88351
+ };
88352
+ // Layer 1: CSS transitions and animations - instant, not removed
88353
+ // (duration 0s preserves final state; 'none' would prevent keyframes from completing)
88354
+ // The rule goes in a constructed stylesheet on document.adoptedStyleSheets, which adds no node to the DOM:
88355
+ // a <style> element changed what the test sees (locator('style') and 'head > *' counts, document.styleSheets,
88356
+ // an iframe's empty head), so a spec stock Playwright passes failed. The <style> is the fallback for a
88357
+ // browser with no constructable stylesheets.
88358
+ let adopted = false;
88359
+ try {
88360
+ if (typeof CSSStyleSheet === 'function' && typeof Document === 'function' && 'adoptedStyleSheets' in Document.prototype) {
88361
+ const sheet = new CSSStyleSheet();
88362
+ sheet.replaceSync(${JSON.stringify(css)});
88363
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
88364
+ adopted = true;
88365
+ // A page that assigns the list without spreading it (document.adoptedStyleSheets = [mine]) would drop the
88366
+ // harness sheet and bring animations back: keep it, last, on every assignment. A bad value (not iterable,
88367
+ // or a list the browser refuses) goes to the browser, which throws its own error as the app's (appError).
88368
+ try {
88369
+ const desc = Object.getOwnPropertyDescriptor(Document.prototype, 'adoptedStyleSheets');
88370
+ if (desc && desc.get && desc.set) {
88371
+ Object.defineProperty(document, 'adoptedStyleSheets', {
88372
+ configurable: true,
88373
+ enumerable: desc.enumerable,
88374
+ get() { return desc.get.call(document); },
88375
+ set(value) {
88376
+ try {
88377
+ if (value === null || (typeof value !== 'object' && typeof value !== 'function') || typeof value[Symbol.iterator] !== 'function') {
88378
+ desc.set.call(document, value);
88379
+ return;
88380
+ }
88381
+ const list = [...value];
88382
+ desc.set.call(document, list.includes(sheet) ? list : [...list, sheet]);
88383
+ } catch (e) {
88384
+ throw appError(e);
88385
+ }
88386
+ },
88387
+ });
88388
+ }
88389
+ } catch (e) {}
88390
+ }
88391
+ } catch (e) {}
88392
+ if (!adopted) try {
88393
+ let applied = false;
88394
+ const apply = () => {
88395
+ if (applied) return true;
88396
+ const root = document.head || document.documentElement;
88397
+ if (!root) return false;
88398
+ const style = document.createElement('style');
88399
+ style.textContent = ${JSON.stringify(css)};
88400
+ root.appendChild(style);
88401
+ applied = true;
88402
+ return true;
88403
+ };
88404
+ if (!apply()) {
88405
+ // The document has no root element yet (iframe, about:blank, empty document): wait for it.
88406
+ let observer = null;
88407
+ const stop = () => { try { if (observer) observer.disconnect(); } catch (e) {} };
88408
+ const retry = () => { try { if (apply()) stop(); } catch (e) { stop(); } };
88409
+ try {
88410
+ observer = new MutationObserver(retry);
88411
+ observer.observe(document, { childList: true });
88412
+ } catch (e) {}
88413
+ document.addEventListener('readystatechange', retry);
88414
+ document.addEventListener('DOMContentLoaded', () => { retry(); stop(); }, { once: true });
88415
+ }
88416
+ } catch (e) {}
88417
+ ${opts.hideWebdriver ? `
88418
+ try {
88419
+ Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
88420
+ } catch (e) {}
88421
+ ` : ""}
88422
+ ${finishTransitions ? `
88423
+ // Layer 1b: CSS transitions run, and are finished as they start, so transitionend fires.
88424
+ // - finish() is deferred to a task: called inside the transitionrun dispatch (or a microtask of it) after a
88425
+ // real input event, Chromium finishes the transition but never fires its transitionstart/transitionend.
88426
+ // - setTimeout and performance.now are the ones present when this script runs, before the page's own
88427
+ // scripts: fake timers the page installs later cannot hold a finish back.
88428
+ // - The subtree is walked only for a pseudo-element's transition (its animations hang off the element).
88429
+ // - An element whose same transition restarts more than ${TRANSITION_LOOP_LIMIT} times within ${TRANSITION_LOOP_WINDOW_MS} ms (an app that starts it again on
88430
+ // each transitionend: carousel, pulse) is left to run at its own pace from then on, instead of cycling at
88431
+ // frame rate for the rest of the run.
88432
+ try {
88433
+ const st = setTimeout;
88434
+ const perf = typeof performance === 'object' && performance && typeof performance.now === 'function' ? performance : null;
88435
+ const now = perf ? perf.now.bind(perf) : Date.now;
88436
+ const seen = new WeakMap();
88437
+ const looping = (el, key) => {
88438
+ let byKey = seen.get(el);
88439
+ if (!byKey) { byKey = new Map(); seen.set(el, byKey); }
88440
+ const t = now();
88441
+ let s = byKey.get(key);
88442
+ if (s && s.loop) return true;
88443
+ if (!s || t - s.since > ${TRANSITION_LOOP_WINDOW_MS}) { s = { n: 0, since: t, loop: false }; byKey.set(key, s); }
88444
+ s.n += 1;
88445
+ if (s.n > ${TRANSITION_LOOP_LIMIT}) s.loop = true;
88446
+ return s.loop;
88447
+ };
88448
+ window.addEventListener('transitionrun', (ev) => {
88449
+ try {
88450
+ const el = ev && ev.target;
88451
+ if (!el || typeof el.getAnimations !== 'function' || typeof CSSTransition !== 'function') return;
88452
+ const pseudo = ev.pseudoElement || '';
88453
+ if (looping(el, ev.propertyName + '|' + pseudo)) return;
88454
+ for (const a of (pseudo ? el.getAnimations({ subtree: true }) : el.getAnimations())) {
88455
+ if (a instanceof CSSTransition && a.transitionProperty === ev.propertyName && a.effect
88456
+ && a.effect.target === el && (a.effect.pseudoElement || '') === pseudo) {
88457
+ st(() => { try { a.finish(); } catch (e) {} }, 0);
88458
+ }
88459
+ }
88460
+ } catch (e) {}
88461
+ }, true);
88462
+ } catch (e) {}
88463
+ ` : ""}
88464
+ // Layer 2: Web Animations API - make instant instead of no-op
88465
+ // (preserves animation completion callbacks that app code may depend on)
88466
+ // Timing the browser refuses (a negative, NaN or non-'auto' duration, a delay that is not finite) goes to the
88467
+ // browser as the page wrote it: zeroed, it raised no TypeError, so a page error stock Playwright reports was hidden.
88468
+ // Options that are not a plain object (an array, a class instance) keep what they inherit: the browser reads
88469
+ // every timing member through the prototype chain, and an array's inherited fill() is a TypeError. A copy kept
88470
+ // only own properties, so the error was hidden; a proxy overrides only duration and delay.
88471
+ try {
88472
+ const origAnimate = Element.prototype.animate;
88473
+ const refused = (options) => {
88474
+ try {
88475
+ if (options === undefined || options === null) return false;
88476
+ if (typeof options !== 'object' && typeof options !== 'function') return !(Number(options) >= 0);
88477
+ const duration = options.duration;
88478
+ if (duration !== undefined && duration !== 'auto' && !(typeof duration === 'number' && duration >= 0)) return true;
88479
+ const delay = options.delay;
88480
+ return delay !== undefined && !Number.isFinite(Number(delay));
88481
+ } catch (e) {
88482
+ return true;
88483
+ }
88484
+ };
88485
+ const instant = (options) => {
88486
+ try {
88487
+ // Plain: its prototype is null or an Object.prototype (of any frame's realm), so it inherits no timing.
88488
+ const proto = Object.getPrototypeOf(options);
88489
+ if (proto !== null && Object.getPrototypeOf(proto) !== null && typeof Proxy === 'function') {
88490
+ // The proxy's target is an empty object, not the options: a target with a read-only, non-configurable
88491
+ // duration or delay (a frozen instance, Object.create with a descriptor) would make returning 0 a proxy
88492
+ // invariant TypeError. Every other member is read from the options, the options as the receiver.
88493
+ return new Proxy({}, { get: (_target, key) => (key === 'duration' || key === 'delay' ? 0 : Reflect.get(options, key)) });
88494
+ }
88495
+ } catch (e) {}
88496
+ return Object.assign({}, options, { duration: 0, delay: 0 });
88497
+ };
88498
+ if (typeof origAnimate === 'function') {
88499
+ Element.prototype.animate = function (keyframes, options) {
88500
+ try {
88501
+ if (refused(options)) return origAnimate.call(this, keyframes, options);
88502
+ if (typeof options === 'object' && options !== null) {
88503
+ options = instant(options);
88504
+ } else {
88505
+ options = 0; // covers number, undefined, and any other type
88506
+ }
88507
+ return origAnimate.call(this, keyframes, options);
88508
+ } catch (e) {
88509
+ throw appError(e);
88510
+ }
88511
+ };
88512
+ }
88513
+ } catch (e) {}
88514
+ })();`);
88515
+ }
88516
+ function isHarnessInitLocation(url) {
88517
+ return url === exports2.HARNESS_INIT_SOURCE_URL;
88518
+ }
88519
+ var MAX_MATCHED_LINE_CHARS = 2048;
88520
+ var MAX_SCANNED_LINES = 200;
88521
+ var STACK_FRAME_RE = /^\s*at\s+(?:[^()\s][^()]*\()?([^()\s]+):\d+:\d+\)?\s*$/;
88522
+ function isHarnessInitErrorStack(stack) {
88523
+ if (!stack)
88524
+ return false;
88525
+ const lines = stack.split("\n", MAX_SCANNED_LINES);
88526
+ for (const line of lines) {
88527
+ if (line.length > MAX_MATCHED_LINE_CHARS)
88528
+ continue;
88529
+ const match = STACK_FRAME_RE.exec(line);
88530
+ if (match)
88531
+ return match[1] === exports2.HARNESS_INIT_SOURCE_URL;
88532
+ }
88533
+ return false;
88534
+ }
88535
+ var MCP_CONSOLE_LINE_LOCATION_RE = / @ ([^\s@]+):\d+$/;
88536
+ function isHarnessInitConsoleLine(line) {
88537
+ const trimmed = line.trim();
88538
+ if (trimmed.length > MAX_MATCHED_LINE_CHARS)
88539
+ return false;
88540
+ const match = MCP_CONSOLE_LINE_LOCATION_RE.exec(trimmed);
88541
+ return match !== null && match[1] === exports2.HARNESS_INIT_SOURCE_URL;
88542
+ }
88543
+ var MCP_LEVEL_LINE_RE = /^\s*(?:-\s+)?\[([A-Za-z]+)\]/;
88544
+ var MCP_FRAME_LINE_RE = /^\s+at\s/;
88545
+ var MCP_TOTALS_RE = /^(\s*Total messages:\s*)(\d+)(\s*\(Errors:\s*)(\d+)(,\s*Warnings:\s*)(\d+)(\).*)$/;
88546
+ var MCP_RETURNING_RE = /^(\s*Returning\s+)(\d+)(\s+messages\b.*)$/;
88547
+ function stripHarnessConsoleEntries(text) {
88548
+ if (!text.includes(exports2.HARNESS_INIT_SOURCE_URL))
88549
+ return text;
88550
+ const lines = text.split("\n");
88551
+ const out = [];
88552
+ let removed = 0;
88553
+ let removedErrors = 0;
88554
+ let removedWarnings = 0;
88555
+ let i = 0;
88556
+ while (i < lines.length) {
88557
+ const line = lines[i];
88558
+ const level = MCP_LEVEL_LINE_RE.exec(line);
88559
+ if (level && isHarnessInitConsoleLine(line.replace(/^\s*-\s+/, ""))) {
88560
+ removed++;
88561
+ const kind = level[1].toLowerCase();
88562
+ if (kind === "error")
88563
+ removedErrors++;
88564
+ else if (kind === "warning" || kind === "warn")
88565
+ removedWarnings++;
88566
+ i++;
88567
+ continue;
88568
+ }
88569
+ if (MCP_FRAME_LINE_RE.test(line)) {
88570
+ let end = i;
88571
+ while (end < lines.length && MCP_FRAME_LINE_RE.test(lines[end]))
88572
+ end++;
88573
+ const frames = lines.slice(i, end);
88574
+ if (isHarnessInitErrorStack(frames.join("\n"))) {
88575
+ const previous = out[out.length - 1];
88576
+ if (previous !== void 0 && previous.trim() !== "" && !/^\s*(?:#|Total messages|Returning)/.test(previous) && !MCP_LEVEL_LINE_RE.test(previous))
88577
+ out.pop();
88578
+ removed++;
88579
+ removedErrors++;
88580
+ } else {
88581
+ out.push(...frames);
88582
+ }
88583
+ i = end;
88584
+ continue;
88585
+ }
88586
+ out.push(line);
88587
+ i++;
88588
+ }
88589
+ if (removed === 0)
88590
+ return text;
88591
+ return out.map((line) => {
88592
+ const totals = MCP_TOTALS_RE.exec(line);
88593
+ if (totals) {
88594
+ const [, a, total, b, errors2, c, warnings, d] = totals;
88595
+ return `${a}${Math.max(0, +total - removed)}${b}${Math.max(0, +errors2 - removedErrors)}${c}${Math.max(0, +warnings - removedWarnings)}${d}`;
88596
+ }
88597
+ const returning = MCP_RETURNING_RE.exec(line);
88598
+ return returning ? `${returning[1]}${Math.max(0, +returning[2] - removed)}${returning[3]}` : line;
88599
+ }).join("\n");
88600
+ }
87554
88601
  }
87555
88602
  });
87556
88603