@validate.qa/runner 1.0.63 → 1.0.65

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 (3) hide show
  1. package/dist/cli.js +214 -8
  2. package/dist/cli.mjs +214 -8
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -3485,6 +3485,135 @@ var init_audit_pricing = __esm({
3485
3485
  }
3486
3486
  });
3487
3487
 
3488
+ // ../shared/dist/audit-grouping.js
3489
+ var GROUPABLE_CALL_TYPES, GROUP_LABELS, FLOW_PHASE_TYPES, FLOW_SERVER_TYPES, FLOW_PHASE_LABELS;
3490
+ var init_audit_grouping = __esm({
3491
+ "../shared/dist/audit-grouping.js"() {
3492
+ "use strict";
3493
+ GROUPABLE_CALL_TYPES = /* @__PURE__ */ new Set([
3494
+ "healIteration",
3495
+ "healOrchestrator",
3496
+ "generateRegressionSuite",
3497
+ "discoveryExplore",
3498
+ "surveyExplore",
3499
+ "featureExplore",
3500
+ "discoveryGenerate",
3501
+ "discoveryPlan",
3502
+ "discoveryGenerateSkeleton",
3503
+ "discoveryGenerateElementContext",
3504
+ "liveGenerate",
3505
+ "scenarioGenerate",
3506
+ "liveSuiteHeal",
3507
+ "captureIteration",
3508
+ "healAPITest",
3509
+ "generateAPITests",
3510
+ // Auth-prologue login-capture run (runLoginCaptureRun) — per-turn rows share a
3511
+ // groupId so they collapse into one "Login Capture" phase card on /audit.
3512
+ "authCapture",
3513
+ // Assistant chat — rounds of one conversation collapse into one card.
3514
+ "assistantChat"
3515
+ ]);
3516
+ GROUP_LABELS = {
3517
+ healIteration: "healSession",
3518
+ healOrchestrator: "healSession",
3519
+ healAPITest: "apiHealSession",
3520
+ generateRegressionSuite: "regressionSession",
3521
+ generateAPITests: "apiTestGenSession",
3522
+ discoveryExplore: "discoveryExploreSession",
3523
+ surveyExplore: "surveyExploreSession",
3524
+ featureExplore: "featureExploreSession",
3525
+ discoveryGenerate: "discoveryGenerateSession",
3526
+ discoveryGenerateSkeleton: "discoveryGenerateSession",
3527
+ discoveryGenerateElementContext: "discoveryGenerateSession",
3528
+ liveGenerate: "liveGenerateSession",
3529
+ scenarioGenerate: "scenarioGenerateSession",
3530
+ discoveryPlan: "discoveryPlanSession",
3531
+ liveSuiteHeal: "liveSuiteHealSession",
3532
+ captureIteration: "captureIterationSession",
3533
+ authCapture: "authCaptureSession",
3534
+ assistantChat: "assistantChatSession"
3535
+ };
3536
+ FLOW_PHASE_TYPES = /* @__PURE__ */ new Set([
3537
+ "surveyExploreSession",
3538
+ "featureExploreSession",
3539
+ "discoveryAIPlanner",
3540
+ "discoveryPlanSession",
3541
+ "scenarioGenerateSession",
3542
+ "liveGenerateSession",
3543
+ "discoveryGenerateSession",
3544
+ // Raw callTypes (for ungrouped single-turn entries like planner)
3545
+ "surveyExplore",
3546
+ "featureExplore",
3547
+ "discoveryPlan",
3548
+ "scenarioGenerate",
3549
+ "liveGenerate",
3550
+ "discoveryGenerate",
3551
+ // Planner repair / retry / strengthen-gate — the one-shot extra planner call
3552
+ // shares the base planner's discoveryFlowId, so it nests as its own phase
3553
+ // instead of dangling as a raw camelCase row.
3554
+ "discoveryAIPlannerRepair",
3555
+ "discoveryAIPlannerRetry",
3556
+ "discoveryAIPlannerStrengthenGate",
3557
+ // Deep-run feature analyst insight (one call per feature discovery).
3558
+ "featureInsight",
3559
+ // Discovery v2 Call-1-authoring pivot — per-test Call 2 spawns. Each session
3560
+ // has its OWN sessionId but shares the parent discoveryFlowId, so grouping by
3561
+ // flowId pulls them under the same Discovery Flow card. `Cleanup` is the
3562
+ // teardown sibling of Fix/Author and nests the same way.
3563
+ "discoveryCall2Fix",
3564
+ "discoveryCall2Author",
3565
+ "discoveryCall2Cleanup",
3566
+ // Auth-prologue login-capture run — grouped label + raw callType.
3567
+ "authCaptureSession",
3568
+ "authCapture"
3569
+ ]);
3570
+ FLOW_SERVER_TYPES = /* @__PURE__ */ new Set([
3571
+ "healthAnalysis",
3572
+ "apiSecurityAudit",
3573
+ "apiSecurityAuditVerify",
3574
+ "generateAPITests",
3575
+ "apiTestGenSession",
3576
+ "healAPITest",
3577
+ "apiHealSession",
3578
+ "healSession",
3579
+ "healIteration"
3580
+ ]);
3581
+ FLOW_PHASE_LABELS = {
3582
+ surveyExploreSession: "Survey Explore",
3583
+ featureExploreSession: "Feature Explore",
3584
+ discoveryAIPlanner: "Feature Analyst",
3585
+ discoveryAIPlannerRepair: "Planner Repair",
3586
+ discoveryAIPlannerRetry: "Planner Retry",
3587
+ discoveryAIPlannerStrengthenGate: "Planner Strengthen Gate",
3588
+ discoveryPlanSession: "Survey Planner",
3589
+ scenarioGenerateSession: "Scenario Gen",
3590
+ liveGenerateSession: "Live Browser Gen",
3591
+ discoveryGenerateSession: "Test Generation",
3592
+ surveyExplore: "Survey Explore",
3593
+ featureExplore: "Feature Explore",
3594
+ discoveryPlan: "Planner",
3595
+ scenarioGenerate: "Scenario Gen",
3596
+ liveGenerate: "Live Browser Gen",
3597
+ discoveryGenerate: "Test Generation",
3598
+ discoveryCall2Fix: "Call 2 Fix",
3599
+ discoveryCall2Author: "Call 2 Author",
3600
+ discoveryCall2Cleanup: "Call 2 Cleanup",
3601
+ featureInsight: "Feature Insight",
3602
+ healthAnalysis: "Health Analysis",
3603
+ apiSecurityAudit: "Security Audit",
3604
+ apiSecurityAuditVerify: "Security Verify",
3605
+ generateAPITests: "API Test Gen",
3606
+ apiTestGenSession: "API Test Gen",
3607
+ healAPITest: "API Test Heal",
3608
+ apiHealSession: "API Test Heal",
3609
+ healSession: "Heal Session",
3610
+ healIteration: "Heal",
3611
+ authCapture: "Login Capture",
3612
+ authCaptureSession: "Login Capture"
3613
+ };
3614
+ }
3615
+ });
3616
+
3488
3617
  // ../shared/dist/discovery-har-decision.js
3489
3618
  function decideHarPosted(response) {
3490
3619
  if (!response)
@@ -5770,6 +5899,53 @@ var init_analytics_url = __esm({
5770
5899
  }
5771
5900
  });
5772
5901
 
5902
+ // ../shared/dist/api-endpoint-hygiene.js
5903
+ function isAssetLikePath(path) {
5904
+ return ASSET_SEGMENT_RE.test(path ?? "");
5905
+ }
5906
+ var ASSET_SEGMENT_RE;
5907
+ var init_api_endpoint_hygiene = __esm({
5908
+ "../shared/dist/api-endpoint-hygiene.js"() {
5909
+ "use strict";
5910
+ ASSET_SEGMENT_RE = /(?:^|\/)(?:assets|static|public|img|images|media|fonts|_next|locales)(?:\/|$)/i;
5911
+ }
5912
+ });
5913
+
5914
+ // ../shared/dist/auth-provider-hosts.js
5915
+ function isAuthProviderHost(host) {
5916
+ if (!host)
5917
+ return false;
5918
+ const h = host.toLowerCase();
5919
+ return AUTH_PROVIDER_HOST_SIGNATURES.some((sig) => sig.startsWith(".") ? h.endsWith(sig) : h.includes(sig));
5920
+ }
5921
+ var AUTH_PROVIDER_HOST_SIGNATURES, AUTH_PATH_SIGNATURE;
5922
+ var init_auth_provider_hosts = __esm({
5923
+ "../shared/dist/auth-provider-hosts.js"() {
5924
+ "use strict";
5925
+ AUTH_PROVIDER_HOST_SIGNATURES = [
5926
+ "clerk.",
5927
+ // clerk.<app-domain> — Clerk Frontend API
5928
+ ".auth0.com",
5929
+ ".okta.com",
5930
+ ".oktapreview.com",
5931
+ "cognito-idp.",
5932
+ ".amazoncognito.com",
5933
+ "identitytoolkit.googleapis.com",
5934
+ // Firebase Auth
5935
+ "securetoken.googleapis.com",
5936
+ ".supabase.co",
5937
+ ".stytch.com",
5938
+ ".workos.com",
5939
+ ".frontegg.com",
5940
+ ".kinde.com",
5941
+ "login.microsoftonline.com",
5942
+ ".b2clogin.com",
5943
+ "accounts.google.com"
5944
+ ];
5945
+ AUTH_PATH_SIGNATURE = /(?:sign[_-]?ins?|first_factor|second_factor|prepare_verification|\/oauth\/|\/authorize\b|\/authenticate\b|openid-connect\/token|\/userinfo\b|\/passwordless)/i;
5946
+ }
5947
+ });
5948
+
5773
5949
  // ../shared/dist/index.js
5774
5950
  var dist_exports = {};
5775
5951
  __export(dist_exports, {
@@ -5781,6 +5957,8 @@ __export(dist_exports, {
5781
5957
  API_AUTH_HEADERS_ENV_KEY: () => API_AUTH_HEADERS_ENV_KEY,
5782
5958
  API_PREFIX: () => API_PREFIX,
5783
5959
  AUDIO_CHUNK_DURATION_MS: () => AUDIO_CHUNK_DURATION_MS,
5960
+ AUTH_PATH_SIGNATURE: () => AUTH_PATH_SIGNATURE,
5961
+ AUTH_PROVIDER_HOST_SIGNATURES: () => AUTH_PROVIDER_HOST_SIGNATURES,
5784
5962
  AUTH_STATE_EXPIRY_SKEW_MS: () => AUTH_STATE_EXPIRY_SKEW_MS,
5785
5963
  AUTH_TAG_COOKIE_STATE: () => AUTH_TAG_COOKIE_STATE,
5786
5964
  AUTH_TAG_LOGIN_BEARER: () => AUTH_TAG_LOGIN_BEARER,
@@ -5801,6 +5979,11 @@ __export(dist_exports, {
5801
5979
  DEFAULT_UI_VARIANT: () => DEFAULT_UI_VARIANT,
5802
5980
  EMAIL_FROM_IDENTITIES: () => EMAIL_FROM_IDENTITIES,
5803
5981
  ENVIRONMENTAL_ERROR_PREFIXES: () => ENVIRONMENTAL_ERROR_PREFIXES,
5982
+ FLOW_PHASE_LABELS: () => FLOW_PHASE_LABELS,
5983
+ FLOW_PHASE_TYPES: () => FLOW_PHASE_TYPES,
5984
+ FLOW_SERVER_TYPES: () => FLOW_SERVER_TYPES,
5985
+ GROUPABLE_CALL_TYPES: () => GROUPABLE_CALL_TYPES,
5986
+ GROUP_LABELS: () => GROUP_LABELS,
5804
5987
  LEGACY_TTS_VOICE_ID_MAP: () => LEGACY_TTS_VOICE_ID_MAP,
5805
5988
  MAX_CREDIT_TOPUP_CENTS: () => MAX_CREDIT_TOPUP_CENTS,
5806
5989
  MAX_DEEPLINK_LENGTH: () => MAX_DEEPLINK_LENGTH,
@@ -5898,6 +6081,8 @@ __export(dist_exports, {
5898
6081
  isAddonType: () => isAddonType,
5899
6082
  isApiTest: () => isApiTest,
5900
6083
  isAppOriginResolutionEnabled: () => isAppOriginResolutionEnabled,
6084
+ isAssetLikePath: () => isAssetLikePath,
6085
+ isAuthProviderHost: () => isAuthProviderHost,
5901
6086
  isAuthReadinessGuardEnabled: () => isAuthReadinessGuardEnabled,
5902
6087
  isAuthTokenName: () => isAuthTokenName,
5903
6088
  isBlockedEnvVar: () => isBlockedEnvVar,
@@ -5982,6 +6167,7 @@ var init_dist = __esm({
5982
6167
  init_mobile_deeplink();
5983
6168
  init_plans();
5984
6169
  init_audit_pricing();
6170
+ init_audit_grouping();
5985
6171
  init_discovery_har_decision();
5986
6172
  init_assertion_classifier();
5987
6173
  init_assertion_strength();
@@ -5998,6 +6184,8 @@ var init_dist = __esm({
5998
6184
  init_feature_flags();
5999
6185
  init_env_flags();
6000
6186
  init_analytics_url();
6187
+ init_api_endpoint_hygiene();
6188
+ init_auth_provider_hosts();
6001
6189
  }
6002
6190
  });
6003
6191
 
@@ -85216,6 +85404,7 @@ var require_api_call_redaction = __commonJS({
85216
85404
  exports2.buildResponseExcerpt = buildResponseExcerpt;
85217
85405
  exports2.isTextExcerptable = isTextExcerptable;
85218
85406
  exports2.buildApiCallSummary = buildApiCallSummary2;
85407
+ var shared_1 = (init_dist(), __toCommonJS(dist_exports));
85219
85408
  exports2.STATIC_EXTENSIONS = /* @__PURE__ */ new Set([
85220
85409
  ".js",
85221
85410
  ".css",
@@ -85233,7 +85422,9 @@ var require_api_call_redaction = __commonJS({
85233
85422
  ".webp",
85234
85423
  ".avif",
85235
85424
  ".mp4",
85236
- ".webm"
85425
+ ".webm",
85426
+ ".lottie",
85427
+ ".webmanifest"
85237
85428
  ]);
85238
85429
  exports2.SECURITY_HEADER_NAMES = /* @__PURE__ */ new Set([
85239
85430
  // CORS & core security
@@ -85322,6 +85513,8 @@ var require_api_call_redaction = __commonJS({
85322
85513
  return false;
85323
85514
  if (path.includes("/api/") || path.includes("/graphql"))
85324
85515
  return true;
85516
+ if (ext.toLowerCase() === ".json" && (0, shared_1.isAssetLikePath)(path))
85517
+ return false;
85325
85518
  if (contentType?.includes("application/json"))
85326
85519
  return true;
85327
85520
  if (contentType?.includes("application/graphql"))
@@ -86876,6 +87069,7 @@ var require_playwright_native = __commonJS({
86876
87069
  runDir = (0, path_1.join)(RUNS_DIR, (0, crypto_1.randomUUID)());
86877
87070
  await (0, promises_1.mkdir)(runDir, { recursive: true });
86878
87071
  const isApiTest2 = playwrightCode.includes("@api") || playwrightCode.includes("{ request }") && !playwrightCode.includes("{ page");
87072
+ const configBaseUrl = isApiTest2 && options.apiBaseUrl ? options.apiBaseUrl : baseUrl;
86879
87073
  const usesRealInboxWait = !isApiTest2 && (0, inbox_stamp_js_1.codeUsesInboxHelper)(playwrightCode);
86880
87074
  const selfProvisionsRegistration = !isApiTest2 && (0, inbox_stamp_js_1.codeSelfProvisionsRegistration)(playwrightCode);
86881
87075
  const effectiveStandingMode = isApiTest2 ? authStandingMode ?? "legacy-if-configured" : "none";
@@ -87079,7 +87273,7 @@ test.afterEach(async ({ page }, testInfo) => {
87079
87273
  if (!importMatch) {
87080
87274
  logs2.push("\u26A0 Could not find @playwright/test import - animation disabling NOT injected");
87081
87275
  }
87082
- const useScopedApiHeaders = isApiTest2 && !!resolvedExtraHeaders && !!importMatch && !!baseUrl;
87276
+ const useScopedApiHeaders = isApiTest2 && !!resolvedExtraHeaders && !!importMatch && !!configBaseUrl;
87083
87277
  const authDelivery = { standingHeadersConfigured: !!resolvedExtraHeaders, standingHeadersInjected: useScopedApiHeaders };
87084
87278
  if (resolvedExtraHeaders && !useScopedApiHeaders) {
87085
87279
  logs2.push("\u26A0 Standing API auth headers were configured but could not be installed origin-scoped \u2014 failing closed (no global fallback).");
@@ -87100,10 +87294,10 @@ test.afterEach(async ({ page }, testInfo) => {
87100
87294
  }
87101
87295
  await (0, promises_1.writeFile)((0, path_1.join)(runDir, "test.spec.ts"), instrumentedCode);
87102
87296
  if (useScopedApiHeaders && resolvedExtraHeaders) {
87103
- await (0, promises_1.writeFile)((0, path_1.join)(runDir, "qa-api-fixtures.ts"), (0, api_auth_headers_js_1.buildOriginScopedApiFixtures)(resolvedExtraHeaders, baseUrl));
87297
+ await (0, promises_1.writeFile)((0, path_1.join)(runDir, "qa-api-fixtures.ts"), (0, api_auth_headers_js_1.buildOriginScopedApiFixtures)(resolvedExtraHeaders, configBaseUrl));
87104
87298
  }
87105
87299
  const headless = (0, shared_1.parseBoolFlag)(process.env.PLAYWRIGHT_HEADLESS) || process.env.NODE_ENV === "production" || IS_CLOUD2;
87106
- logs2.push(`\u2699\uFE0F Config: baseURL=${JSON.stringify(baseUrl)}, headless=${headless}${isApiTest2 ? ", API mode (no screenshot/video)" : ""}`);
87300
+ logs2.push(`\u2699\uFE0F Config: baseURL=${JSON.stringify(configBaseUrl)}, headless=${headless}${isApiTest2 ? ", API mode (no screenshot/video)" : ""}${configBaseUrl !== baseUrl ? ` (elected @api origin; app BASE_URL ${JSON.stringify(baseUrl)})` : ""}`);
87107
87301
  const absTestDir = runDir.replace(/\\/g, "/");
87108
87302
  const absResultsFile = (0, path_1.join)(runDir, "results.json").replace(/\\/g, "/");
87109
87303
  const absOutputDir = (0, path_1.join)(runDir, "test-results").replace(/\\/g, "/");
@@ -87127,7 +87321,7 @@ module.exports = {
87127
87321
  timeout: ${usesRealInboxWait ? 24e4 : 6e4},
87128
87322
  retries: 0,
87129
87323
  use: {
87130
- baseURL: ${JSON.stringify(baseUrl)},
87324
+ baseURL: ${JSON.stringify(configBaseUrl)},
87131
87325
  ${storageStateLine}
87132
87326
  ${extraHeadersLine}
87133
87327
  browserName: 'chromium',
@@ -87187,6 +87381,9 @@ module.exports = {
87187
87381
  }
87188
87382
  }
87189
87383
  const childTestVariables = { ...testVariables ?? {} };
87384
+ if (isApiTest2 && options.apiBaseUrl && !childTestVariables["API_BASE_URL"]) {
87385
+ childTestVariables["API_BASE_URL"] = options.apiBaseUrl;
87386
+ }
87190
87387
  const trustedRuntimeEnv = {
87191
87388
  ...isolation.env,
87192
87389
  // Generated callbacks receive only a random loopback capability. The global
@@ -350858,7 +351055,7 @@ var require_package4 = __commonJS({
350858
351055
  "package.json"(exports2, module2) {
350859
351056
  module2.exports = {
350860
351057
  name: "@validate.qa/runner",
350861
- version: "1.0.63",
351058
+ version: "1.0.65",
350862
351059
  description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
350863
351060
  bin: {
350864
351061
  "validate-runner": "dist/cli.js"
@@ -358454,6 +358651,7 @@ async function executeRun(run2, opts, headers, requestLiveBrowserHeartbeat = ()
358454
358651
  const runNativeWithLiveBrowser = (nativeOptions) => (0, import_runner_core.executeTestViaNativePlaywright)({
358455
358652
  authStandingMode: authPlan.standingHeadersMode,
358456
358653
  storageStatePath: wrapperStorageStatePath,
358654
+ apiBaseUrl: run2.apiBaseUrl,
358457
358655
  ...nativeOptions,
358458
358656
  liveBrowserHandle,
358459
358657
  onLiveBrowserSnapshot: requestLiveBrowserHeartbeat
@@ -358864,7 +359062,9 @@ ${finalVerifyResult.logs ?? ""}`;
358864
359062
  } else {
358865
359063
  const xo = resolveCrossOriginConfigOverride({
358866
359064
  appOriginEnabled: isAppOriginResolutionEnabled(),
358867
- baseUrl: run2.baseUrl,
359065
+ // An @api run anchored on an elected execution origin must be judged against THAT
359066
+ // origin, or a correctly-anchored call reads as "wrong origin" (gate-review tweak).
359067
+ baseUrl: run2.apiBaseUrl ?? run2.baseUrl,
358868
359068
  composedError,
358869
359069
  verdicts: enrichedSteps.map((s) => s.verdict),
358870
359070
  apiCalls: rawSteps.flatMap((s) => s.apiCalls ?? []),
@@ -359451,13 +359651,14 @@ ${finalVerifyResult.logs ?? ""}`;
359451
359651
  }
359452
359652
  }
359453
359653
  }
359654
+ const auditStatus = info.exitReason === "abort" || info.exitReason === "timeout" ? "ERROR" : "SUCCESS";
359454
359655
  pushAudit({
359455
359656
  projectId: ctx.projectId,
359456
359657
  sessionId: ctx.sessionId,
359457
359658
  callType: info.callType,
359458
359659
  model: info.model,
359459
359660
  provider: info.provider,
359460
- status: "SUCCESS",
359661
+ status: auditStatus,
359461
359662
  durationMs: info.durationMs,
359462
359663
  promptTokens: info.promptTokens,
359463
359664
  completionTokens: info.completionTokens,
@@ -359474,6 +359675,11 @@ ${finalVerifyResult.logs ?? ""}`;
359474
359675
  discoveryFlowId: run2.runId,
359475
359676
  ...info.callType === "authCapture" ? { featureName: "Login" } : ctx.featureName ? { featureName: ctx.featureName } : {},
359476
359677
  ...ctx.mode ? { discoveryMode: ctx.mode } : {},
359678
+ // Per-session identity for Call-2 rows (parity with execute-run.ts) so
359679
+ // Fix/Author/Cleanup phases are distinguishable in the flow card.
359680
+ ...info.scenarioName ? { scenarioName: info.scenarioName } : {},
359681
+ ...info.sessionKind ? { sessionKind: info.sessionKind } : {},
359682
+ ...info.exitReason ? { exitReason: info.exitReason } : {},
359477
359683
  ...cliCostUsd !== void 0 ? { cliCostUsd } : {},
359478
359684
  ...ledgerAudit ? {
359479
359685
  ledger: ledgerAudit.entries,
package/dist/cli.mjs CHANGED
@@ -3490,6 +3490,135 @@ var init_audit_pricing = __esm({
3490
3490
  }
3491
3491
  });
3492
3492
 
3493
+ // ../shared/dist/audit-grouping.js
3494
+ var GROUPABLE_CALL_TYPES, GROUP_LABELS, FLOW_PHASE_TYPES, FLOW_SERVER_TYPES, FLOW_PHASE_LABELS;
3495
+ var init_audit_grouping = __esm({
3496
+ "../shared/dist/audit-grouping.js"() {
3497
+ "use strict";
3498
+ GROUPABLE_CALL_TYPES = /* @__PURE__ */ new Set([
3499
+ "healIteration",
3500
+ "healOrchestrator",
3501
+ "generateRegressionSuite",
3502
+ "discoveryExplore",
3503
+ "surveyExplore",
3504
+ "featureExplore",
3505
+ "discoveryGenerate",
3506
+ "discoveryPlan",
3507
+ "discoveryGenerateSkeleton",
3508
+ "discoveryGenerateElementContext",
3509
+ "liveGenerate",
3510
+ "scenarioGenerate",
3511
+ "liveSuiteHeal",
3512
+ "captureIteration",
3513
+ "healAPITest",
3514
+ "generateAPITests",
3515
+ // Auth-prologue login-capture run (runLoginCaptureRun) — per-turn rows share a
3516
+ // groupId so they collapse into one "Login Capture" phase card on /audit.
3517
+ "authCapture",
3518
+ // Assistant chat — rounds of one conversation collapse into one card.
3519
+ "assistantChat"
3520
+ ]);
3521
+ GROUP_LABELS = {
3522
+ healIteration: "healSession",
3523
+ healOrchestrator: "healSession",
3524
+ healAPITest: "apiHealSession",
3525
+ generateRegressionSuite: "regressionSession",
3526
+ generateAPITests: "apiTestGenSession",
3527
+ discoveryExplore: "discoveryExploreSession",
3528
+ surveyExplore: "surveyExploreSession",
3529
+ featureExplore: "featureExploreSession",
3530
+ discoveryGenerate: "discoveryGenerateSession",
3531
+ discoveryGenerateSkeleton: "discoveryGenerateSession",
3532
+ discoveryGenerateElementContext: "discoveryGenerateSession",
3533
+ liveGenerate: "liveGenerateSession",
3534
+ scenarioGenerate: "scenarioGenerateSession",
3535
+ discoveryPlan: "discoveryPlanSession",
3536
+ liveSuiteHeal: "liveSuiteHealSession",
3537
+ captureIteration: "captureIterationSession",
3538
+ authCapture: "authCaptureSession",
3539
+ assistantChat: "assistantChatSession"
3540
+ };
3541
+ FLOW_PHASE_TYPES = /* @__PURE__ */ new Set([
3542
+ "surveyExploreSession",
3543
+ "featureExploreSession",
3544
+ "discoveryAIPlanner",
3545
+ "discoveryPlanSession",
3546
+ "scenarioGenerateSession",
3547
+ "liveGenerateSession",
3548
+ "discoveryGenerateSession",
3549
+ // Raw callTypes (for ungrouped single-turn entries like planner)
3550
+ "surveyExplore",
3551
+ "featureExplore",
3552
+ "discoveryPlan",
3553
+ "scenarioGenerate",
3554
+ "liveGenerate",
3555
+ "discoveryGenerate",
3556
+ // Planner repair / retry / strengthen-gate — the one-shot extra planner call
3557
+ // shares the base planner's discoveryFlowId, so it nests as its own phase
3558
+ // instead of dangling as a raw camelCase row.
3559
+ "discoveryAIPlannerRepair",
3560
+ "discoveryAIPlannerRetry",
3561
+ "discoveryAIPlannerStrengthenGate",
3562
+ // Deep-run feature analyst insight (one call per feature discovery).
3563
+ "featureInsight",
3564
+ // Discovery v2 Call-1-authoring pivot — per-test Call 2 spawns. Each session
3565
+ // has its OWN sessionId but shares the parent discoveryFlowId, so grouping by
3566
+ // flowId pulls them under the same Discovery Flow card. `Cleanup` is the
3567
+ // teardown sibling of Fix/Author and nests the same way.
3568
+ "discoveryCall2Fix",
3569
+ "discoveryCall2Author",
3570
+ "discoveryCall2Cleanup",
3571
+ // Auth-prologue login-capture run — grouped label + raw callType.
3572
+ "authCaptureSession",
3573
+ "authCapture"
3574
+ ]);
3575
+ FLOW_SERVER_TYPES = /* @__PURE__ */ new Set([
3576
+ "healthAnalysis",
3577
+ "apiSecurityAudit",
3578
+ "apiSecurityAuditVerify",
3579
+ "generateAPITests",
3580
+ "apiTestGenSession",
3581
+ "healAPITest",
3582
+ "apiHealSession",
3583
+ "healSession",
3584
+ "healIteration"
3585
+ ]);
3586
+ FLOW_PHASE_LABELS = {
3587
+ surveyExploreSession: "Survey Explore",
3588
+ featureExploreSession: "Feature Explore",
3589
+ discoveryAIPlanner: "Feature Analyst",
3590
+ discoveryAIPlannerRepair: "Planner Repair",
3591
+ discoveryAIPlannerRetry: "Planner Retry",
3592
+ discoveryAIPlannerStrengthenGate: "Planner Strengthen Gate",
3593
+ discoveryPlanSession: "Survey Planner",
3594
+ scenarioGenerateSession: "Scenario Gen",
3595
+ liveGenerateSession: "Live Browser Gen",
3596
+ discoveryGenerateSession: "Test Generation",
3597
+ surveyExplore: "Survey Explore",
3598
+ featureExplore: "Feature Explore",
3599
+ discoveryPlan: "Planner",
3600
+ scenarioGenerate: "Scenario Gen",
3601
+ liveGenerate: "Live Browser Gen",
3602
+ discoveryGenerate: "Test Generation",
3603
+ discoveryCall2Fix: "Call 2 Fix",
3604
+ discoveryCall2Author: "Call 2 Author",
3605
+ discoveryCall2Cleanup: "Call 2 Cleanup",
3606
+ featureInsight: "Feature Insight",
3607
+ healthAnalysis: "Health Analysis",
3608
+ apiSecurityAudit: "Security Audit",
3609
+ apiSecurityAuditVerify: "Security Verify",
3610
+ generateAPITests: "API Test Gen",
3611
+ apiTestGenSession: "API Test Gen",
3612
+ healAPITest: "API Test Heal",
3613
+ apiHealSession: "API Test Heal",
3614
+ healSession: "Heal Session",
3615
+ healIteration: "Heal",
3616
+ authCapture: "Login Capture",
3617
+ authCaptureSession: "Login Capture"
3618
+ };
3619
+ }
3620
+ });
3621
+
3493
3622
  // ../shared/dist/discovery-har-decision.js
3494
3623
  function decideHarPosted(response) {
3495
3624
  if (!response)
@@ -5775,6 +5904,53 @@ var init_analytics_url = __esm({
5775
5904
  }
5776
5905
  });
5777
5906
 
5907
+ // ../shared/dist/api-endpoint-hygiene.js
5908
+ function isAssetLikePath(path) {
5909
+ return ASSET_SEGMENT_RE.test(path ?? "");
5910
+ }
5911
+ var ASSET_SEGMENT_RE;
5912
+ var init_api_endpoint_hygiene = __esm({
5913
+ "../shared/dist/api-endpoint-hygiene.js"() {
5914
+ "use strict";
5915
+ ASSET_SEGMENT_RE = /(?:^|\/)(?:assets|static|public|img|images|media|fonts|_next|locales)(?:\/|$)/i;
5916
+ }
5917
+ });
5918
+
5919
+ // ../shared/dist/auth-provider-hosts.js
5920
+ function isAuthProviderHost(host) {
5921
+ if (!host)
5922
+ return false;
5923
+ const h = host.toLowerCase();
5924
+ return AUTH_PROVIDER_HOST_SIGNATURES.some((sig) => sig.startsWith(".") ? h.endsWith(sig) : h.includes(sig));
5925
+ }
5926
+ var AUTH_PROVIDER_HOST_SIGNATURES, AUTH_PATH_SIGNATURE;
5927
+ var init_auth_provider_hosts = __esm({
5928
+ "../shared/dist/auth-provider-hosts.js"() {
5929
+ "use strict";
5930
+ AUTH_PROVIDER_HOST_SIGNATURES = [
5931
+ "clerk.",
5932
+ // clerk.<app-domain> — Clerk Frontend API
5933
+ ".auth0.com",
5934
+ ".okta.com",
5935
+ ".oktapreview.com",
5936
+ "cognito-idp.",
5937
+ ".amazoncognito.com",
5938
+ "identitytoolkit.googleapis.com",
5939
+ // Firebase Auth
5940
+ "securetoken.googleapis.com",
5941
+ ".supabase.co",
5942
+ ".stytch.com",
5943
+ ".workos.com",
5944
+ ".frontegg.com",
5945
+ ".kinde.com",
5946
+ "login.microsoftonline.com",
5947
+ ".b2clogin.com",
5948
+ "accounts.google.com"
5949
+ ];
5950
+ AUTH_PATH_SIGNATURE = /(?:sign[_-]?ins?|first_factor|second_factor|prepare_verification|\/oauth\/|\/authorize\b|\/authenticate\b|openid-connect\/token|\/userinfo\b|\/passwordless)/i;
5951
+ }
5952
+ });
5953
+
5778
5954
  // ../shared/dist/index.js
5779
5955
  var dist_exports = {};
5780
5956
  __export(dist_exports, {
@@ -5786,6 +5962,8 @@ __export(dist_exports, {
5786
5962
  API_AUTH_HEADERS_ENV_KEY: () => API_AUTH_HEADERS_ENV_KEY,
5787
5963
  API_PREFIX: () => API_PREFIX,
5788
5964
  AUDIO_CHUNK_DURATION_MS: () => AUDIO_CHUNK_DURATION_MS,
5965
+ AUTH_PATH_SIGNATURE: () => AUTH_PATH_SIGNATURE,
5966
+ AUTH_PROVIDER_HOST_SIGNATURES: () => AUTH_PROVIDER_HOST_SIGNATURES,
5789
5967
  AUTH_STATE_EXPIRY_SKEW_MS: () => AUTH_STATE_EXPIRY_SKEW_MS,
5790
5968
  AUTH_TAG_COOKIE_STATE: () => AUTH_TAG_COOKIE_STATE,
5791
5969
  AUTH_TAG_LOGIN_BEARER: () => AUTH_TAG_LOGIN_BEARER,
@@ -5806,6 +5984,11 @@ __export(dist_exports, {
5806
5984
  DEFAULT_UI_VARIANT: () => DEFAULT_UI_VARIANT,
5807
5985
  EMAIL_FROM_IDENTITIES: () => EMAIL_FROM_IDENTITIES,
5808
5986
  ENVIRONMENTAL_ERROR_PREFIXES: () => ENVIRONMENTAL_ERROR_PREFIXES,
5987
+ FLOW_PHASE_LABELS: () => FLOW_PHASE_LABELS,
5988
+ FLOW_PHASE_TYPES: () => FLOW_PHASE_TYPES,
5989
+ FLOW_SERVER_TYPES: () => FLOW_SERVER_TYPES,
5990
+ GROUPABLE_CALL_TYPES: () => GROUPABLE_CALL_TYPES,
5991
+ GROUP_LABELS: () => GROUP_LABELS,
5809
5992
  LEGACY_TTS_VOICE_ID_MAP: () => LEGACY_TTS_VOICE_ID_MAP,
5810
5993
  MAX_CREDIT_TOPUP_CENTS: () => MAX_CREDIT_TOPUP_CENTS,
5811
5994
  MAX_DEEPLINK_LENGTH: () => MAX_DEEPLINK_LENGTH,
@@ -5903,6 +6086,8 @@ __export(dist_exports, {
5903
6086
  isAddonType: () => isAddonType,
5904
6087
  isApiTest: () => isApiTest,
5905
6088
  isAppOriginResolutionEnabled: () => isAppOriginResolutionEnabled,
6089
+ isAssetLikePath: () => isAssetLikePath,
6090
+ isAuthProviderHost: () => isAuthProviderHost,
5906
6091
  isAuthReadinessGuardEnabled: () => isAuthReadinessGuardEnabled,
5907
6092
  isAuthTokenName: () => isAuthTokenName,
5908
6093
  isBlockedEnvVar: () => isBlockedEnvVar,
@@ -5987,6 +6172,7 @@ var init_dist = __esm({
5987
6172
  init_mobile_deeplink();
5988
6173
  init_plans();
5989
6174
  init_audit_pricing();
6175
+ init_audit_grouping();
5990
6176
  init_discovery_har_decision();
5991
6177
  init_assertion_classifier();
5992
6178
  init_assertion_strength();
@@ -6003,6 +6189,8 @@ var init_dist = __esm({
6003
6189
  init_feature_flags();
6004
6190
  init_env_flags();
6005
6191
  init_analytics_url();
6192
+ init_api_endpoint_hygiene();
6193
+ init_auth_provider_hosts();
6006
6194
  }
6007
6195
  });
6008
6196
 
@@ -85221,6 +85409,7 @@ var require_api_call_redaction = __commonJS({
85221
85409
  exports.buildResponseExcerpt = buildResponseExcerpt;
85222
85410
  exports.isTextExcerptable = isTextExcerptable;
85223
85411
  exports.buildApiCallSummary = buildApiCallSummary2;
85412
+ var shared_1 = (init_dist(), __toCommonJS(dist_exports));
85224
85413
  exports.STATIC_EXTENSIONS = /* @__PURE__ */ new Set([
85225
85414
  ".js",
85226
85415
  ".css",
@@ -85238,7 +85427,9 @@ var require_api_call_redaction = __commonJS({
85238
85427
  ".webp",
85239
85428
  ".avif",
85240
85429
  ".mp4",
85241
- ".webm"
85430
+ ".webm",
85431
+ ".lottie",
85432
+ ".webmanifest"
85242
85433
  ]);
85243
85434
  exports.SECURITY_HEADER_NAMES = /* @__PURE__ */ new Set([
85244
85435
  // CORS & core security
@@ -85327,6 +85518,8 @@ var require_api_call_redaction = __commonJS({
85327
85518
  return false;
85328
85519
  if (path.includes("/api/") || path.includes("/graphql"))
85329
85520
  return true;
85521
+ if (ext.toLowerCase() === ".json" && (0, shared_1.isAssetLikePath)(path))
85522
+ return false;
85330
85523
  if (contentType?.includes("application/json"))
85331
85524
  return true;
85332
85525
  if (contentType?.includes("application/graphql"))
@@ -86881,6 +87074,7 @@ var require_playwright_native = __commonJS({
86881
87074
  runDir = (0, path_1.join)(RUNS_DIR, (0, crypto_1.randomUUID)());
86882
87075
  await (0, promises_1.mkdir)(runDir, { recursive: true });
86883
87076
  const isApiTest2 = playwrightCode.includes("@api") || playwrightCode.includes("{ request }") && !playwrightCode.includes("{ page");
87077
+ const configBaseUrl = isApiTest2 && options.apiBaseUrl ? options.apiBaseUrl : baseUrl;
86884
87078
  const usesRealInboxWait = !isApiTest2 && (0, inbox_stamp_js_1.codeUsesInboxHelper)(playwrightCode);
86885
87079
  const selfProvisionsRegistration = !isApiTest2 && (0, inbox_stamp_js_1.codeSelfProvisionsRegistration)(playwrightCode);
86886
87080
  const effectiveStandingMode = isApiTest2 ? authStandingMode ?? "legacy-if-configured" : "none";
@@ -87084,7 +87278,7 @@ test.afterEach(async ({ page }, testInfo) => {
87084
87278
  if (!importMatch) {
87085
87279
  logs2.push("\u26A0 Could not find @playwright/test import - animation disabling NOT injected");
87086
87280
  }
87087
- const useScopedApiHeaders = isApiTest2 && !!resolvedExtraHeaders && !!importMatch && !!baseUrl;
87281
+ const useScopedApiHeaders = isApiTest2 && !!resolvedExtraHeaders && !!importMatch && !!configBaseUrl;
87088
87282
  const authDelivery = { standingHeadersConfigured: !!resolvedExtraHeaders, standingHeadersInjected: useScopedApiHeaders };
87089
87283
  if (resolvedExtraHeaders && !useScopedApiHeaders) {
87090
87284
  logs2.push("\u26A0 Standing API auth headers were configured but could not be installed origin-scoped \u2014 failing closed (no global fallback).");
@@ -87105,10 +87299,10 @@ test.afterEach(async ({ page }, testInfo) => {
87105
87299
  }
87106
87300
  await (0, promises_1.writeFile)((0, path_1.join)(runDir, "test.spec.ts"), instrumentedCode);
87107
87301
  if (useScopedApiHeaders && resolvedExtraHeaders) {
87108
- await (0, promises_1.writeFile)((0, path_1.join)(runDir, "qa-api-fixtures.ts"), (0, api_auth_headers_js_1.buildOriginScopedApiFixtures)(resolvedExtraHeaders, baseUrl));
87302
+ await (0, promises_1.writeFile)((0, path_1.join)(runDir, "qa-api-fixtures.ts"), (0, api_auth_headers_js_1.buildOriginScopedApiFixtures)(resolvedExtraHeaders, configBaseUrl));
87109
87303
  }
87110
87304
  const headless = (0, shared_1.parseBoolFlag)(process.env.PLAYWRIGHT_HEADLESS) || process.env.NODE_ENV === "production" || IS_CLOUD2;
87111
- logs2.push(`\u2699\uFE0F Config: baseURL=${JSON.stringify(baseUrl)}, headless=${headless}${isApiTest2 ? ", API mode (no screenshot/video)" : ""}`);
87305
+ logs2.push(`\u2699\uFE0F Config: baseURL=${JSON.stringify(configBaseUrl)}, headless=${headless}${isApiTest2 ? ", API mode (no screenshot/video)" : ""}${configBaseUrl !== baseUrl ? ` (elected @api origin; app BASE_URL ${JSON.stringify(baseUrl)})` : ""}`);
87112
87306
  const absTestDir = runDir.replace(/\\/g, "/");
87113
87307
  const absResultsFile = (0, path_1.join)(runDir, "results.json").replace(/\\/g, "/");
87114
87308
  const absOutputDir = (0, path_1.join)(runDir, "test-results").replace(/\\/g, "/");
@@ -87132,7 +87326,7 @@ module.exports = {
87132
87326
  timeout: ${usesRealInboxWait ? 24e4 : 6e4},
87133
87327
  retries: 0,
87134
87328
  use: {
87135
- baseURL: ${JSON.stringify(baseUrl)},
87329
+ baseURL: ${JSON.stringify(configBaseUrl)},
87136
87330
  ${storageStateLine}
87137
87331
  ${extraHeadersLine}
87138
87332
  browserName: 'chromium',
@@ -87192,6 +87386,9 @@ module.exports = {
87192
87386
  }
87193
87387
  }
87194
87388
  const childTestVariables = { ...testVariables ?? {} };
87389
+ if (isApiTest2 && options.apiBaseUrl && !childTestVariables["API_BASE_URL"]) {
87390
+ childTestVariables["API_BASE_URL"] = options.apiBaseUrl;
87391
+ }
87195
87392
  const trustedRuntimeEnv = {
87196
87393
  ...isolation.env,
87197
87394
  // Generated callbacks receive only a random loopback capability. The global
@@ -350863,7 +351060,7 @@ var require_package4 = __commonJS({
350863
351060
  "package.json"(exports, module) {
350864
351061
  module.exports = {
350865
351062
  name: "@validate.qa/runner",
350866
- version: "1.0.63",
351063
+ version: "1.0.65",
350867
351064
  description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
350868
351065
  bin: {
350869
351066
  "validate-runner": "dist/cli.js"
@@ -358462,6 +358659,7 @@ async function executeRun(run2, opts, headers, requestLiveBrowserHeartbeat = ()
358462
358659
  const runNativeWithLiveBrowser = (nativeOptions) => (0, import_runner_core.executeTestViaNativePlaywright)({
358463
358660
  authStandingMode: authPlan.standingHeadersMode,
358464
358661
  storageStatePath: wrapperStorageStatePath,
358662
+ apiBaseUrl: run2.apiBaseUrl,
358465
358663
  ...nativeOptions,
358466
358664
  liveBrowserHandle,
358467
358665
  onLiveBrowserSnapshot: requestLiveBrowserHeartbeat
@@ -358872,7 +359070,9 @@ ${finalVerifyResult.logs ?? ""}`;
358872
359070
  } else {
358873
359071
  const xo = resolveCrossOriginConfigOverride({
358874
359072
  appOriginEnabled: isAppOriginResolutionEnabled(),
358875
- baseUrl: run2.baseUrl,
359073
+ // An @api run anchored on an elected execution origin must be judged against THAT
359074
+ // origin, or a correctly-anchored call reads as "wrong origin" (gate-review tweak).
359075
+ baseUrl: run2.apiBaseUrl ?? run2.baseUrl,
358876
359076
  composedError,
358877
359077
  verdicts: enrichedSteps.map((s) => s.verdict),
358878
359078
  apiCalls: rawSteps.flatMap((s) => s.apiCalls ?? []),
@@ -359459,13 +359659,14 @@ ${finalVerifyResult.logs ?? ""}`;
359459
359659
  }
359460
359660
  }
359461
359661
  }
359662
+ const auditStatus = info.exitReason === "abort" || info.exitReason === "timeout" ? "ERROR" : "SUCCESS";
359462
359663
  pushAudit({
359463
359664
  projectId: ctx.projectId,
359464
359665
  sessionId: ctx.sessionId,
359465
359666
  callType: info.callType,
359466
359667
  model: info.model,
359467
359668
  provider: info.provider,
359468
- status: "SUCCESS",
359669
+ status: auditStatus,
359469
359670
  durationMs: info.durationMs,
359470
359671
  promptTokens: info.promptTokens,
359471
359672
  completionTokens: info.completionTokens,
@@ -359482,6 +359683,11 @@ ${finalVerifyResult.logs ?? ""}`;
359482
359683
  discoveryFlowId: run2.runId,
359483
359684
  ...info.callType === "authCapture" ? { featureName: "Login" } : ctx.featureName ? { featureName: ctx.featureName } : {},
359484
359685
  ...ctx.mode ? { discoveryMode: ctx.mode } : {},
359686
+ // Per-session identity for Call-2 rows (parity with execute-run.ts) so
359687
+ // Fix/Author/Cleanup phases are distinguishable in the flow card.
359688
+ ...info.scenarioName ? { scenarioName: info.scenarioName } : {},
359689
+ ...info.sessionKind ? { sessionKind: info.sessionKind } : {},
359690
+ ...info.exitReason ? { exitReason: info.exitReason } : {},
359485
359691
  ...cliCostUsd !== void 0 ? { cliCostUsd } : {},
359486
359692
  ...ledgerAudit ? {
359487
359693
  ledger: ledgerAudit.entries,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@validate.qa/runner",
3
- "version": "1.0.63",
3
+ "version": "1.0.65",
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"