@validate.qa/runner 1.0.102 → 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.
package/dist/cli.mjs CHANGED
@@ -33,7 +33,7 @@ import {
33
33
  require_resource_governor,
34
34
  require_result_api_call_bodies,
35
35
  require_result_shed
36
- } from "./chunk-DN3O6EYT.mjs";
36
+ } from "./chunk-CG75I3ZZ.mjs";
37
37
  import {
38
38
  require_tar_isolated
39
39
  } from "./chunk-AAUHXGDS.mjs";
@@ -41,15 +41,15 @@ import "./chunk-EXRFHATN.mjs";
41
41
  import {
42
42
  require_browser_config,
43
43
  require_cloud_runner
44
- } from "./chunk-ZZYBRIBT.mjs";
45
- import "./chunk-BXLEPT4M.mjs";
46
- import "./chunk-5DFBEL72.mjs";
47
- import "./chunk-CBVHPYVJ.mjs";
48
- import "./chunk-HJ6FKHMM.mjs";
49
- import "./chunk-PWFWMVZU.mjs";
50
- import "./chunk-E2VEMBQQ.mjs";
51
- import "./chunk-4WLP3RWN.mjs";
52
- import "./chunk-E2RRAE27.mjs";
44
+ } from "./chunk-CMM4DOXC.mjs";
45
+ import "./chunk-NNJYKR3O.mjs";
46
+ import "./chunk-YIM746WO.mjs";
47
+ import "./chunk-WVLBQDFP.mjs";
48
+ import "./chunk-7SH3COMQ.mjs";
49
+ import "./chunk-TK5KEGNW.mjs";
50
+ import "./chunk-YI3ZC2XI.mjs";
51
+ import "./chunk-BECCGMU4.mjs";
52
+ import "./chunk-36AWDKPL.mjs";
53
53
  import {
54
54
  LIVE_FRAME_IDLE_PUSH_INTERVAL_MS,
55
55
  MOBILE_COLD_BOOT_SETTLE_MS,
@@ -84,7 +84,7 @@ import {
84
84
  stepAppliesToPlatform,
85
85
  stepsMissingPlatformLocators,
86
86
  stepsNeedingReviewForPlatform
87
- } from "./chunk-3JM6XTSZ.mjs";
87
+ } from "./chunk-OTKJZIOD.mjs";
88
88
  import {
89
89
  __commonJS,
90
90
  __require,
@@ -252,7 +252,7 @@ var require_package = __commonJS({
252
252
  "package.json"(exports, module) {
253
253
  module.exports = {
254
254
  name: "@validate.qa/runner",
255
- version: "1.0.102",
255
+ version: "1.0.103",
256
256
  description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
257
257
  bin: {
258
258
  "validate-runner": "dist/cli.js"
@@ -261,7 +261,7 @@ var require_package = __commonJS({
261
261
  build: "tsup",
262
262
  dev: "tsx src/cli.ts",
263
263
  lint: "eslint .",
264
- test: "node --import tsx --test src/supervisor-child-argv.test.ts src/audit-scrub.test.ts src/retry-on-429.test.ts src/version-check.test.ts src/poll-interval.test.ts src/node-version-notice.test.ts src/services/auth-state.test.ts src/run-no-longer-active.test.ts src/mode-abort-no-verdict.test.ts src/discovery-render-type.test.ts src/batch-heal-no-start.test.ts src/heartbeat-active-runs.test.ts src/result-post-structure-413.test.ts src/result-shed-routes-413.test.ts src/result-post-retry.test.ts src/result-media-stale-epoch.test.ts src/run-write-guard-bundle.test.ts src/spec-driver-bundle.test.ts src/mcp-servers-bundle.test.ts src/codex-files-mcp-server-bundle.test.ts src/claim-nonce-not-logged.test.ts src/sign-in-capture.test.ts src/run-status-verdicts.test.ts src/sign-in-failure.test.ts src/mobile-ui-path-succeeded.test.ts src/supervisor-policy.test.ts src/login-check.test.ts src/heal-post-lost-ack.test.ts src/ui-heal-result-video.test.ts src/heal-log-stream-chunks.test.ts src/shutdown-claim-window.test.ts src/result-post-redeploy-budget.test.ts src/shutdown-signals.test.ts",
264
+ test: "node --import tsx --test src/supervisor-child-argv.test.ts src/audit-scrub.test.ts src/retry-on-429.test.ts src/version-check.test.ts src/poll-interval.test.ts src/node-version-notice.test.ts src/services/auth-state.test.ts src/run-no-longer-active.test.ts src/mode-abort-no-verdict.test.ts src/discovery-render-type.test.ts src/batch-heal-no-start.test.ts src/heartbeat-active-runs.test.ts src/result-post-structure-413.test.ts src/result-shed-routes-413.test.ts src/result-post-retry.test.ts src/result-media-stale-epoch.test.ts src/run-write-guard-bundle.test.ts src/run-write-guard-bundle-skip.test.ts src/spec-driver-bundle.test.ts src/mcp-servers-bundle.test.ts src/codex-files-mcp-server-bundle.test.ts src/claim-nonce-not-logged.test.ts src/sign-in-capture.test.ts src/run-status-verdicts.test.ts src/sign-in-failure.test.ts src/mobile-ui-path-succeeded.test.ts src/supervisor-policy.test.ts src/login-check.test.ts src/heal-post-lost-ack.test.ts src/ui-heal-result-video.test.ts src/heal-log-stream-chunks.test.ts src/shutdown-claim-window.test.ts src/result-post-redeploy-budget.test.ts src/shutdown-signals.test.ts",
265
265
  "test:mobile": "node --import tsx --test --test-concurrency=2 src/runner.mobile-ai-env.test.ts src/runner.mobile-heal-audit-wiring.test.ts src/mobile-device-queue.test.ts src/device-lease.test.ts src/runner-watchdog.test.ts src/result-payload-budget.test.ts src/result-media-upload.test.ts src/services/appium-*.test.ts src/services/*mobile*.test.ts src/services/mobile/*.test.ts",
266
266
  "smoke:installed": "node scripts/installed-run-smoke.mjs",
267
267
  "check:packed-mcp": "node scripts/packed-install-check.mjs",
@@ -10588,10 +10588,14 @@ ${finalVerifyResult.logs ?? ""}`;
10588
10588
  const allAppBug = stepCount > 0 && enrichedSteps.every((s) => s.verdict === "app_bug");
10589
10589
  const hasFixFailed = enrichedSteps.some((s) => s.verdict === "fix_failed");
10590
10590
  const hasPass = enrichedSteps.some((s) => s.verdict === "pass");
10591
- const passed = stepCount > 0 ? finalVerifyResult?.passed === true && hasPass && !hasFixFailed && !allAppBug : finalVerifyResult?.passed === true;
10591
+ const verifyPassed = stepCount > 0 ? finalVerifyResult?.passed === true && hasPass && !hasFixFailed && !allAppBug : finalVerifyResult?.passed === true;
10592
+ const partialSkipError = verifyPassed ? (0, import_runner_core7.apiHealPartialSkipError)(enrichedSteps) : void 0;
10593
+ const passed = verifyPassed && partialSkipError === void 0;
10592
10594
  let composedError;
10593
10595
  if (!passed) {
10594
- if (finalUnfixableReason) {
10596
+ if (partialSkipError !== void 0) {
10597
+ composedError = partialSkipError;
10598
+ } else if (finalUnfixableReason) {
10595
10599
  composedError = `Unfixable after ${attemptsUsed} attempt(s): ${finalUnfixableReason}`;
10596
10600
  } else if (allAppBug || enrichedSteps.some((s) => s.verdict === "app_bug") && !hasFixFailed) {
10597
10601
  const bugs = enrichedSteps.filter((s) => s.verdict === "app_bug");
@@ -10790,6 +10794,7 @@ ${finalVerifyResult.logs ?? ""}`;
10790
10794
  [audit] ${drainErr instanceof Error ? drainErr.message : String(drainErr)}`;
10791
10795
  console.warn(`[runner]${auditDrainError}`);
10792
10796
  }
10797
+ const relabelSteps = result2.relabelEvidenceSteps ?? result2.stepResults;
10793
10798
  const isConfigIssue = !result2.passed && isConfigLikeReason(classifyErrorMessage(result2.error ?? null));
10794
10799
  let healStatus = (0, import_runner_core7.healRunStatus)({ passed: result2.passed, error: result2.error, isConfigIssue });
10795
10800
  let healError = result2.error;
@@ -10800,7 +10805,7 @@ ${finalVerifyResult.logs ?? ""}`;
10800
10805
  tags: run2.tags ?? [],
10801
10806
  error: result2.error,
10802
10807
  logs: result2.logs,
10803
- stepResults: result2.stepResults,
10808
+ stepResults: relabelSteps,
10804
10809
  apiCalls: result2.apiCalls
10805
10810
  });
10806
10811
  if (rateLimitRelabel) {
@@ -10814,7 +10819,7 @@ ${finalVerifyResult.logs ?? ""}`;
10814
10819
  tags: run2.tags ?? [],
10815
10820
  error: healError,
10816
10821
  logs: result2.logs,
10817
- stepResults: result2.stepResults
10822
+ stepResults: relabelSteps
10818
10823
  });
10819
10824
  if (loginRelabel) {
10820
10825
  healStatus = loginRelabel.status;
@@ -10948,15 +10953,16 @@ ${finalVerifyResult.logs ?? ""}`;
10948
10953
  authStandingMode: failedTestPlan.standingHeadersMode,
10949
10954
  validateContext: callbackContext
10950
10955
  });
10956
+ const verdict = (0, import_runner_core7.apiHealVerifyVerdict)(verifyResult);
10951
10957
  results.push({
10952
10958
  testCaseId: failedTest.testCaseId,
10953
- status: verifyResult.passed ? "fixed" : "unfixable",
10954
- error: verifyResult.error,
10959
+ status: verdict.fixed ? "fixed" : "unfixable",
10960
+ error: verdict.error,
10955
10961
  diagnosis: "API test healed via server-side AI",
10956
- healedCode: verifyResult.passed ? healedCode : void 0,
10962
+ healedCode: verdict.fixed ? healedCode : void 0,
10957
10963
  duration: Date.now() - testStart
10958
10964
  });
10959
- if (verifyResult.passed) {
10965
+ if (verdict.fixed) {
10960
10966
  log.ok(` Fixed: ${failedTest.name}`);
10961
10967
  } else {
10962
10968
  log.fail(` Could not fix: ${failedTest.name}`);
@@ -14207,7 +14213,7 @@ program.command("start").description("Start the local test runner").option("--to
14207
14213
  console.warn("\u26A0 --tunnel has no effect without --mobile (the mobile bridge it exposes only starts with --mobile). Ignoring --tunnel.");
14208
14214
  }
14209
14215
  {
14210
- const { hasLocalGoogleLogin, GOOGLE_LOGIN_COMMAND } = await import("./dist-52M5RDZ6.mjs");
14216
+ const { hasLocalGoogleLogin, GOOGLE_LOGIN_COMMAND } = await import("./dist-2G7YTNH5.mjs");
14211
14217
  if (process.stdout.isTTY && !await hasLocalGoogleLogin()) {
14212
14218
  console.log(`Tip: for apps with "Continue with Google", sign in to Google once on this machine: ${GOOGLE_LOGIN_COMMAND}`);
14213
14219
  }
@@ -14237,7 +14243,7 @@ async function askLine(question) {
14237
14243
  }
14238
14244
  }
14239
14245
  program.command("google-login").description('Sign in to a TEST Google account once on this machine, for tests of apps with "Continue with Google"').option("--site <url...>", "The app address(es) this sign-in may be used for, e.g. https://app.example.com").option("--allow-file-storage", "Keep the sign-in in a plain file (only you can read it) when the system keychain is not available").option("--yes", "Confirm without asking that this is a dedicated test Google account").action(async (opts) => {
14240
- const { signInLocalGoogle, LOCAL_GOOGLE_WARNING, grantedSite, defaultStoreKind } = await import("./dist-52M5RDZ6.mjs");
14246
+ const { signInLocalGoogle, LOCAL_GOOGLE_WARNING, grantedSite, defaultStoreKind } = await import("./dist-2G7YTNH5.mjs");
14241
14247
  console.log(LOCAL_GOOGLE_WARNING);
14242
14248
  if (!opts.yes) {
14243
14249
  const answer = await askLine('Is this a dedicated test Google account? Type "yes" to continue: ');
@@ -14272,7 +14278,7 @@ program.command("google-login").description('Sign in to a TEST Google account on
14272
14278
  }
14273
14279
  });
14274
14280
  program.command("google-allow-site <url>").description("Let the Google sign-in on this machine be used for one more app address").action(async (url) => {
14275
- const { allowLocalGoogleSites, grantedSite } = await import("./dist-52M5RDZ6.mjs");
14281
+ const { allowLocalGoogleSites, grantedSite } = await import("./dist-2G7YTNH5.mjs");
14276
14282
  const site = grantedSite(url);
14277
14283
  if (!site) {
14278
14284
  console.error("\u2717 Not a web address: give it like https://app.example.com");
@@ -14286,7 +14292,7 @@ program.command("google-allow-site <url>").description("Let the Google sign-in o
14286
14292
  console.log(`\u2713 Tests on this machine may now use the Google sign-in for: ${sites.join(", ")}`);
14287
14293
  });
14288
14294
  program.command("google-logout").description("Forget the Google sign-in saved on this machine").action(async () => {
14289
- const { deleteLocalGoogleLogin } = await import("./dist-52M5RDZ6.mjs");
14295
+ const { deleteLocalGoogleLogin } = await import("./dist-2G7YTNH5.mjs");
14290
14296
  await deleteLocalGoogleLogin();
14291
14297
  console.log("\u2713 Google sign-in removed from this machine");
14292
14298
  });
@@ -6220,7 +6220,7 @@ function namedPagePath(arg) {
6220
6220
  const quoted = /^(['"`])([^'"`]*)\1$/.exec(text);
6221
6221
  const regex = quoted ? null : /^\/((?:\\.|[^/\\])+)\/[a-z]*$/.exec(text);
6222
6222
  if (quoted) {
6223
- path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\*\*$/, "");
6223
+ path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\/\*\*$/, "/");
6224
6224
  } else if (regex) {
6225
6225
  path = regex[1].replace(/^\^/, "").replace(/^\.\*/, "").replace(/\$$/, "").replace(/\\([/.-])/g, "$1");
6226
6226
  } else {
@@ -22792,6 +22792,19 @@ var require_harness_init_script = __commonJS({
22792
22792
  const finishTransitions = opts.finishTransitions ?? (0, shared_1.parseBoolFlag)(process.env.HARNESS_FINISH_CSS_TRANSITIONS);
22793
22793
  const css = finishTransitions ? "*, *::before, *::after { animation-duration: 0s !important; animation-delay: 0s !important; }" : "*, *::before, *::after { transition-duration: 0s !important; animation-duration: 0s !important; transition-delay: 0s !important; animation-delay: 0s !important; }";
22794
22794
  return withHarnessSourceUrl(`(() => {
22795
+ // An error the browser throws for the page's own value, passed through one of the wrappers below (a bad
22796
+ // adoptedStyleSheets list, animate timing it refuses), is the app's: stock Playwright reports it as a page error.
22797
+ // Thrown from our wrapper, its top frame would be this script, and the console evidence drops such an error as
22798
+ // the runner's own. Our frames come out of its stack, so the app's call is the top frame, as without the harness.
22799
+ // (Under Error.stackTraceLimit the stack then has one or two frames fewer than stock's; the top frame is right.)
22800
+ const own = ${JSON.stringify(exports2.HARNESS_INIT_SOURCE_URL)};
22801
+ const appError = (e) => {
22802
+ try {
22803
+ const stack = e !== null && typeof e === 'object' ? e.stack : undefined;
22804
+ if (typeof stack === 'string' && stack.includes(own)) e.stack = stack.split('\\n').filter((line) => !line.includes(own)).join('\\n');
22805
+ } catch (x) {}
22806
+ return e;
22807
+ };
22795
22808
  // Layer 1: CSS transitions and animations - instant, not removed
22796
22809
  // (duration 0s preserves final state; 'none' would prevent keyframes from completing)
22797
22810
  // The rule goes in a constructed stylesheet on document.adoptedStyleSheets, which adds no node to the DOM:
@@ -22805,6 +22818,31 @@ var require_harness_init_script = __commonJS({
22805
22818
  sheet.replaceSync(${JSON.stringify(css)});
22806
22819
  document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
22807
22820
  adopted = true;
22821
+ // A page that assigns the list without spreading it (document.adoptedStyleSheets = [mine]) would drop the
22822
+ // harness sheet and bring animations back: keep it, last, on every assignment. A bad value (not iterable,
22823
+ // or a list the browser refuses) goes to the browser, which throws its own error as the app's (appError).
22824
+ try {
22825
+ const desc = Object.getOwnPropertyDescriptor(Document.prototype, 'adoptedStyleSheets');
22826
+ if (desc && desc.get && desc.set) {
22827
+ Object.defineProperty(document, 'adoptedStyleSheets', {
22828
+ configurable: true,
22829
+ enumerable: desc.enumerable,
22830
+ get() { return desc.get.call(document); },
22831
+ set(value) {
22832
+ try {
22833
+ if (value === null || (typeof value !== 'object' && typeof value !== 'function') || typeof value[Symbol.iterator] !== 'function') {
22834
+ desc.set.call(document, value);
22835
+ return;
22836
+ }
22837
+ const list = [...value];
22838
+ desc.set.call(document, list.includes(sheet) ? list : [...list, sheet]);
22839
+ } catch (e) {
22840
+ throw appError(e);
22841
+ }
22842
+ },
22843
+ });
22844
+ }
22845
+ } catch (e) {}
22808
22846
  }
22809
22847
  } catch (e) {}
22810
22848
  if (!adopted) try {
@@ -22881,16 +22919,51 @@ ${finishTransitions ? `
22881
22919
  ` : ""}
22882
22920
  // Layer 2: Web Animations API - make instant instead of no-op
22883
22921
  // (preserves animation completion callbacks that app code may depend on)
22922
+ // Timing the browser refuses (a negative, NaN or non-'auto' duration, a delay that is not finite) goes to the
22923
+ // browser as the page wrote it: zeroed, it raised no TypeError, so a page error stock Playwright reports was hidden.
22924
+ // Options that are not a plain object (an array, a class instance) keep what they inherit: the browser reads
22925
+ // every timing member through the prototype chain, and an array's inherited fill() is a TypeError. A copy kept
22926
+ // only own properties, so the error was hidden; a proxy overrides only duration and delay.
22884
22927
  try {
22885
22928
  const origAnimate = Element.prototype.animate;
22929
+ const refused = (options) => {
22930
+ try {
22931
+ if (options === undefined || options === null) return false;
22932
+ if (typeof options !== 'object' && typeof options !== 'function') return !(Number(options) >= 0);
22933
+ const duration = options.duration;
22934
+ if (duration !== undefined && duration !== 'auto' && !(typeof duration === 'number' && duration >= 0)) return true;
22935
+ const delay = options.delay;
22936
+ return delay !== undefined && !Number.isFinite(Number(delay));
22937
+ } catch (e) {
22938
+ return true;
22939
+ }
22940
+ };
22941
+ const instant = (options) => {
22942
+ try {
22943
+ // Plain: its prototype is null or an Object.prototype (of any frame's realm), so it inherits no timing.
22944
+ const proto = Object.getPrototypeOf(options);
22945
+ if (proto !== null && Object.getPrototypeOf(proto) !== null && typeof Proxy === 'function') {
22946
+ // The proxy's target is an empty object, not the options: a target with a read-only, non-configurable
22947
+ // duration or delay (a frozen instance, Object.create with a descriptor) would make returning 0 a proxy
22948
+ // invariant TypeError. Every other member is read from the options, the options as the receiver.
22949
+ return new Proxy({}, { get: (_target, key) => (key === 'duration' || key === 'delay' ? 0 : Reflect.get(options, key)) });
22950
+ }
22951
+ } catch (e) {}
22952
+ return Object.assign({}, options, { duration: 0, delay: 0 });
22953
+ };
22886
22954
  if (typeof origAnimate === 'function') {
22887
22955
  Element.prototype.animate = function (keyframes, options) {
22888
- if (typeof options === 'object' && options !== null) {
22889
- options = Object.assign({}, options, { duration: 0, delay: 0 });
22890
- } else {
22891
- options = 0; // covers number, undefined, and any other type
22956
+ try {
22957
+ if (refused(options)) return origAnimate.call(this, keyframes, options);
22958
+ if (typeof options === 'object' && options !== null) {
22959
+ options = instant(options);
22960
+ } else {
22961
+ options = 0; // covers number, undefined, and any other type
22962
+ }
22963
+ return origAnimate.call(this, keyframes, options);
22964
+ } catch (e) {
22965
+ throw appError(e);
22892
22966
  }
22893
- return origAnimate.call(this, keyframes, options);
22894
22967
  };
22895
22968
  }
22896
22969
  } catch (e) {}
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  require_credential_mcp_server
3
- } from "./chunk-ZZYBRIBT.mjs";
4
- import "./chunk-CBVHPYVJ.mjs";
5
- import "./chunk-3JM6XTSZ.mjs";
3
+ } from "./chunk-CMM4DOXC.mjs";
4
+ import "./chunk-WVLBQDFP.mjs";
5
+ import "./chunk-OTKJZIOD.mjs";
6
6
  import {
7
7
  __toESM
8
8
  } from "./chunk-WQXBGQU4.mjs";
@@ -5943,7 +5943,7 @@ function namedPagePath(arg) {
5943
5943
  const quoted = /^(['"`])([^'"`]*)\1$/.exec(text);
5944
5944
  const regex = quoted ? null : /^\/((?:\\.|[^/\\])+)\/[a-z]*$/.exec(text);
5945
5945
  if (quoted) {
5946
- path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\*\*$/, "");
5946
+ path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\/\*\*$/, "/");
5947
5947
  } else if (regex) {
5948
5948
  path = regex[1].replace(/^\^/, "").replace(/^\.\*/, "").replace(/\$$/, "").replace(/\\([/.-])/g, "$1");
5949
5949
  } else {
@@ -21780,9 +21780,10 @@ var require_exploration_parser = __commonJS({
21780
21780
  return false;
21781
21781
  return RECORD_TITLE_WITH_COMPANY.test(name);
21782
21782
  }
21783
- var TYPED_VALUE_CALL_RE = /\.(fill|type|pressSequentially|insertText)\(\s*(['"])(?:\\.|(?!\2)[^\\])*\2(\s*,\s*\{[^{}]*\})?\s*\)/g;
21783
+ var TYPED_VALUE_CALL_RE = /\.(fill|type|pressSequentially|insertText)\(\s*(['"`])(?:\\.|(?!\2)[^\\])*\2(\s*,\s*\{[^{}]*\})?\s*\)/g;
21784
+ var TWO_ARG_TYPED_VALUE_CALL_RE = /\.(fill|type)\(\s*((?:(['"`])(?:\\.|(?!\3)[^\\])*\3|[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*))\s*,\s*(['"`])(?:\\.|(?!\4)[^\\])*\4(\s*,\s*\{[^{}]*\})?\s*\)/g;
21784
21785
  function maskTypedValues(codeLine, mask) {
21785
- return codeLine.replace(TYPED_VALUE_CALL_RE, (_all, method, _quote, options) => `.${method}(${mask}${options ?? ""})`);
21786
+ return codeLine.replace(TWO_ARG_TYPED_VALUE_CALL_RE, (_all, method, selector, _selQuote, _quote, options) => `.${method}(${selector}, ${mask}${options ?? ""})`).replace(TYPED_VALUE_CALL_RE, (_all, method, _quote, options) => `.${method}(${mask}${options ?? ""})`);
21786
21787
  }
21787
21788
  function sanitizePromptCodeLine(codeLine) {
21788
21789
  let sanitized = codeLine;
@@ -24744,7 +24745,7 @@ var require_repo_feature_verification = __commonJS({
24744
24745
  return null;
24745
24746
  }
24746
24747
  function normalizeControlText(text) {
24747
- return text.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
24748
+ return text.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}\p{M}]+/gu, " ").trim();
24748
24749
  }
24749
24750
  var SESSION_END_LABEL = /^(?:log|sign) ?(?:out|off)$/;
24750
24751
  var SESSION_END_SPELLINGS = ["log out", "logout", "log off", "logoff", "sign out", "signout", "sign off", "signoff"];
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  require_repo_feature_verification
3
- } from "./chunk-BXLEPT4M.mjs";
4
- import "./chunk-PWFWMVZU.mjs";
5
- import "./chunk-E2RRAE27.mjs";
6
- import "./chunk-3JM6XTSZ.mjs";
3
+ } from "./chunk-NNJYKR3O.mjs";
4
+ import "./chunk-TK5KEGNW.mjs";
5
+ import "./chunk-36AWDKPL.mjs";
6
+ import "./chunk-OTKJZIOD.mjs";
7
7
  import {
8
8
  __commonJS,
9
9
  __require,
@@ -0,0 +1,17 @@
1
+ import {
2
+ require_dist
3
+ } from "./chunk-CG75I3ZZ.mjs";
4
+ import "./chunk-AAUHXGDS.mjs";
5
+ import "./chunk-EXRFHATN.mjs";
6
+ import "./chunk-CMM4DOXC.mjs";
7
+ import "./chunk-NNJYKR3O.mjs";
8
+ import "./chunk-YIM746WO.mjs";
9
+ import "./chunk-WVLBQDFP.mjs";
10
+ import "./chunk-7SH3COMQ.mjs";
11
+ import "./chunk-TK5KEGNW.mjs";
12
+ import "./chunk-YI3ZC2XI.mjs";
13
+ import "./chunk-BECCGMU4.mjs";
14
+ import "./chunk-36AWDKPL.mjs";
15
+ import "./chunk-OTKJZIOD.mjs";
16
+ import "./chunk-WQXBGQU4.mjs";
17
+ export default require_dist();
@@ -73438,7 +73438,7 @@ function namedPagePath(arg) {
73438
73438
  const quoted = /^(['"`])([^'"`]*)\1$/.exec(text);
73439
73439
  const regex = quoted ? null : /^\/((?:\\.|[^/\\])+)\/[a-z]*$/.exec(text);
73440
73440
  if (quoted) {
73441
- path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\*\*$/, "");
73441
+ path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\/\*\*$/, "/");
73442
73442
  } else if (regex) {
73443
73443
  path = regex[1].replace(/^\^/, "").replace(/^\.\*/, "").replace(/\$$/, "").replace(/\\([/.-])/g, "$1");
73444
73444
  } else {
@@ -88336,6 +88336,19 @@ var require_harness_init_script = __commonJS({
88336
88336
  const finishTransitions = opts.finishTransitions ?? (0, shared_1.parseBoolFlag)(process.env.HARNESS_FINISH_CSS_TRANSITIONS);
88337
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
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
+ };
88339
88352
  // Layer 1: CSS transitions and animations - instant, not removed
88340
88353
  // (duration 0s preserves final state; 'none' would prevent keyframes from completing)
88341
88354
  // The rule goes in a constructed stylesheet on document.adoptedStyleSheets, which adds no node to the DOM:
@@ -88349,6 +88362,31 @@ var require_harness_init_script = __commonJS({
88349
88362
  sheet.replaceSync(${JSON.stringify(css)});
88350
88363
  document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
88351
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) {}
88352
88390
  }
88353
88391
  } catch (e) {}
88354
88392
  if (!adopted) try {
@@ -88425,16 +88463,51 @@ ${finishTransitions ? `
88425
88463
  ` : ""}
88426
88464
  // Layer 2: Web Animations API - make instant instead of no-op
88427
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.
88428
88471
  try {
88429
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
+ };
88430
88498
  if (typeof origAnimate === 'function') {
88431
88499
  Element.prototype.animate = function (keyframes, options) {
88432
- if (typeof options === 'object' && options !== null) {
88433
- options = Object.assign({}, options, { duration: 0, delay: 0 });
88434
- } else {
88435
- options = 0; // covers number, undefined, and any other type
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);
88436
88510
  }
88437
- return origAnimate.call(this, keyframes, options);
88438
88511
  };
88439
88512
  }
88440
88513
  } catch (e) {}
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  require_brokered_inbox_reader,
3
3
  require_cli_inbox_broker
4
- } from "./chunk-5DFBEL72.mjs";
4
+ } from "./chunk-YIM746WO.mjs";
5
5
  import {
6
6
  require_inbox_tools,
7
7
  require_verification_inbox
8
- } from "./chunk-CBVHPYVJ.mjs";
9
- import "./chunk-3JM6XTSZ.mjs";
8
+ } from "./chunk-WVLBQDFP.mjs";
9
+ import "./chunk-OTKJZIOD.mjs";
10
10
  import {
11
11
  __commonJS,
12
12
  __require,
@@ -5943,7 +5943,7 @@ function namedPagePath(arg) {
5943
5943
  const quoted = /^(['"`])([^'"`]*)\1$/.exec(text);
5944
5944
  const regex = quoted ? null : /^\/((?:\\.|[^/\\])+)\/[a-z]*$/.exec(text);
5945
5945
  if (quoted) {
5946
- path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\*\*$/, "");
5946
+ path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\/\*\*$/, "/");
5947
5947
  } else if (regex) {
5948
5948
  path = regex[1].replace(/^\^/, "").replace(/^\.\*/, "").replace(/\$$/, "").replace(/\\([/.-])/g, "$1");
5949
5949
  } else {
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  require_route_seed,
3
3
  require_typescript
4
- } from "./chunk-HJ6FKHMM.mjs";
4
+ } from "./chunk-7SH3COMQ.mjs";
5
5
  import {
6
6
  require_route_manifest
7
- } from "./chunk-PWFWMVZU.mjs";
7
+ } from "./chunk-TK5KEGNW.mjs";
8
8
  import {
9
9
  dist_exports,
10
10
  init_dist
11
- } from "./chunk-3JM6XTSZ.mjs";
11
+ } from "./chunk-OTKJZIOD.mjs";
12
12
  import {
13
13
  __commonJS,
14
14
  __require,
@@ -5943,7 +5943,7 @@ function namedPagePath(arg) {
5943
5943
  const quoted = /^(['"`])([^'"`]*)\1$/.exec(text);
5944
5944
  const regex = quoted ? null : /^\/((?:\\.|[^/\\])+)\/[a-z]*$/.exec(text);
5945
5945
  if (quoted) {
5946
- path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\*\*$/, "");
5946
+ path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\/\*\*$/, "/");
5947
5947
  } else if (regex) {
5948
5948
  path = regex[1].replace(/^\^/, "").replace(/^\.\*/, "").replace(/\$$/, "").replace(/\\([/.-])/g, "$1");
5949
5949
  } else {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  require_run_write_guard_preload
3
- } from "./chunk-E2VEMBQQ.mjs";
4
- import "./chunk-4WLP3RWN.mjs";
5
- import "./chunk-E2RRAE27.mjs";
6
- import "./chunk-3JM6XTSZ.mjs";
3
+ } from "./chunk-YI3ZC2XI.mjs";
4
+ import "./chunk-BECCGMU4.mjs";
5
+ import "./chunk-36AWDKPL.mjs";
6
+ import "./chunk-OTKJZIOD.mjs";
7
7
  import {
8
8
  __toESM
9
9
  } from "./chunk-WQXBGQU4.mjs";
@@ -5943,7 +5943,7 @@ function namedPagePath(arg) {
5943
5943
  const quoted = /^(['"`])([^'"`]*)\1$/.exec(text);
5944
5944
  const regex = quoted ? null : /^\/((?:\\.|[^/\\])+)\/[a-z]*$/.exec(text);
5945
5945
  if (quoted) {
5946
- path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\*\*$/, "");
5946
+ path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\/\*\*$/, "/");
5947
5947
  } else if (regex) {
5948
5948
  path = regex[1].replace(/^\^/, "").replace(/^\.\*/, "").replace(/\$$/, "").replace(/\\([/.-])/g, "$1");
5949
5949
  } else {
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  require_run_write_guard
3
- } from "./chunk-4WLP3RWN.mjs";
4
- import "./chunk-E2RRAE27.mjs";
5
- import "./chunk-3JM6XTSZ.mjs";
3
+ } from "./chunk-BECCGMU4.mjs";
4
+ import "./chunk-36AWDKPL.mjs";
5
+ import "./chunk-OTKJZIOD.mjs";
6
6
  import {
7
7
  __toESM
8
8
  } from "./chunk-WQXBGQU4.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@validate.qa/runner",
3
- "version": "1.0.102",
3
+ "version": "1.0.103",
4
4
  "description": "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
5
5
  "bin": {
6
6
  "validate-runner": "dist/cli.js"
@@ -9,7 +9,7 @@
9
9
  "build": "tsup",
10
10
  "dev": "tsx src/cli.ts",
11
11
  "lint": "eslint .",
12
- "test": "node --import tsx --test src/supervisor-child-argv.test.ts src/audit-scrub.test.ts src/retry-on-429.test.ts src/version-check.test.ts src/poll-interval.test.ts src/node-version-notice.test.ts src/services/auth-state.test.ts src/run-no-longer-active.test.ts src/mode-abort-no-verdict.test.ts src/discovery-render-type.test.ts src/batch-heal-no-start.test.ts src/heartbeat-active-runs.test.ts src/result-post-structure-413.test.ts src/result-shed-routes-413.test.ts src/result-post-retry.test.ts src/result-media-stale-epoch.test.ts src/run-write-guard-bundle.test.ts src/spec-driver-bundle.test.ts src/mcp-servers-bundle.test.ts src/codex-files-mcp-server-bundle.test.ts src/claim-nonce-not-logged.test.ts src/sign-in-capture.test.ts src/run-status-verdicts.test.ts src/sign-in-failure.test.ts src/mobile-ui-path-succeeded.test.ts src/supervisor-policy.test.ts src/login-check.test.ts src/heal-post-lost-ack.test.ts src/ui-heal-result-video.test.ts src/heal-log-stream-chunks.test.ts src/shutdown-claim-window.test.ts src/result-post-redeploy-budget.test.ts src/shutdown-signals.test.ts",
12
+ "test": "node --import tsx --test src/supervisor-child-argv.test.ts src/audit-scrub.test.ts src/retry-on-429.test.ts src/version-check.test.ts src/poll-interval.test.ts src/node-version-notice.test.ts src/services/auth-state.test.ts src/run-no-longer-active.test.ts src/mode-abort-no-verdict.test.ts src/discovery-render-type.test.ts src/batch-heal-no-start.test.ts src/heartbeat-active-runs.test.ts src/result-post-structure-413.test.ts src/result-shed-routes-413.test.ts src/result-post-retry.test.ts src/result-media-stale-epoch.test.ts src/run-write-guard-bundle.test.ts src/run-write-guard-bundle-skip.test.ts src/spec-driver-bundle.test.ts src/mcp-servers-bundle.test.ts src/codex-files-mcp-server-bundle.test.ts src/claim-nonce-not-logged.test.ts src/sign-in-capture.test.ts src/run-status-verdicts.test.ts src/sign-in-failure.test.ts src/mobile-ui-path-succeeded.test.ts src/supervisor-policy.test.ts src/login-check.test.ts src/heal-post-lost-ack.test.ts src/ui-heal-result-video.test.ts src/heal-log-stream-chunks.test.ts src/shutdown-claim-window.test.ts src/result-post-redeploy-budget.test.ts src/shutdown-signals.test.ts",
13
13
  "test:mobile": "node --import tsx --test --test-concurrency=2 src/runner.mobile-ai-env.test.ts src/runner.mobile-heal-audit-wiring.test.ts src/mobile-device-queue.test.ts src/device-lease.test.ts src/runner-watchdog.test.ts src/result-payload-budget.test.ts src/result-media-upload.test.ts src/services/appium-*.test.ts src/services/*mobile*.test.ts src/services/mobile/*.test.ts",
14
14
  "smoke:installed": "node scripts/installed-run-smoke.mjs",
15
15
  "check:packed-mcp": "node scripts/packed-install-check.mjs",
@@ -1,17 +0,0 @@
1
- import {
2
- require_dist
3
- } from "./chunk-DN3O6EYT.mjs";
4
- import "./chunk-AAUHXGDS.mjs";
5
- import "./chunk-EXRFHATN.mjs";
6
- import "./chunk-ZZYBRIBT.mjs";
7
- import "./chunk-BXLEPT4M.mjs";
8
- import "./chunk-5DFBEL72.mjs";
9
- import "./chunk-CBVHPYVJ.mjs";
10
- import "./chunk-HJ6FKHMM.mjs";
11
- import "./chunk-PWFWMVZU.mjs";
12
- import "./chunk-E2VEMBQQ.mjs";
13
- import "./chunk-4WLP3RWN.mjs";
14
- import "./chunk-E2RRAE27.mjs";
15
- import "./chunk-3JM6XTSZ.mjs";
16
- import "./chunk-WQXBGQU4.mjs";
17
- export default require_dist();