@validate.qa/runner 1.0.47 → 1.0.48

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 +210 -8
  2. package/dist/cli.mjs +210 -8
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -5041,6 +5041,191 @@ var init_auth_block_detector = __esm({
5041
5041
  }
5042
5042
  });
5043
5043
 
5044
+ // ../shared/dist/auth-state-freshness.js
5045
+ function isCsrfTokenName(name) {
5046
+ return /csrf|xsrf|forgery|authenticity|requestverification/.test(name.toLowerCase());
5047
+ }
5048
+ function isAuthTokenName(name) {
5049
+ const n = name.toLowerCase();
5050
+ if (isCsrfTokenName(n))
5051
+ return false;
5052
+ if (/session|auth|jwt|credential|identity|login|token/.test(n))
5053
+ return true;
5054
+ if (/(?:^|[^a-z0-9])(?:sid|sess|account|remember)(?:[^a-z0-9]|$)/.test(n))
5055
+ return true;
5056
+ return [
5057
+ "connect.sid",
5058
+ "jsessionid",
5059
+ "phpsessid",
5060
+ "aspnet",
5061
+ "asp.net",
5062
+ "aspxauth",
5063
+ "sb-",
5064
+ "supabase",
5065
+ "amplify",
5066
+ "cognito",
5067
+ "auth0",
5068
+ "okta",
5069
+ "kc-",
5070
+ "li_at",
5071
+ "ltpatoken",
5072
+ "wordpress_logged_in"
5073
+ ].some((frag) => n.includes(frag));
5074
+ }
5075
+ function isSessionClassName(name) {
5076
+ const n = name.toLowerCase();
5077
+ if (isCsrfTokenName(n))
5078
+ return false;
5079
+ if (/session|sess/.test(n))
5080
+ return true;
5081
+ if (/(?:^|[^a-z0-9])sid(?:[^a-z0-9]|$)/.test(n))
5082
+ return true;
5083
+ return [
5084
+ "connect.sid",
5085
+ "jsessionid",
5086
+ "phpsessid",
5087
+ "aspxauth",
5088
+ "li_at",
5089
+ "ltpatoken",
5090
+ "wordpress_logged_in"
5091
+ ].some((frag) => n.includes(frag));
5092
+ }
5093
+ function decodeBase64UrlToUtf8(segment) {
5094
+ if (typeof atob !== "function")
5095
+ return void 0;
5096
+ const base64 = segment.replace(/-/g, "+").replace(/_/g, "/");
5097
+ const padded = base64.length % 4 === 0 ? base64 : base64 + "=".repeat(4 - base64.length % 4);
5098
+ try {
5099
+ const binary = atob(padded);
5100
+ let percentEncoded = "";
5101
+ for (let i = 0; i < binary.length; i += 1) {
5102
+ percentEncoded += `%${binary.charCodeAt(i).toString(16).padStart(2, "0")}`;
5103
+ }
5104
+ return decodeURIComponent(percentEncoded);
5105
+ } catch {
5106
+ return void 0;
5107
+ }
5108
+ }
5109
+ function jwtExpiryMs(value) {
5110
+ const parts = value.split(".");
5111
+ if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2])
5112
+ return void 0;
5113
+ const json = decodeBase64UrlToUtf8(parts[1]);
5114
+ if (json === void 0)
5115
+ return void 0;
5116
+ try {
5117
+ const payload = JSON.parse(json);
5118
+ const exp = payload?.exp;
5119
+ return typeof exp === "number" && Number.isFinite(exp) && exp > 0 ? exp * 1e3 : void 0;
5120
+ } catch {
5121
+ return void 0;
5122
+ }
5123
+ }
5124
+ function localStorageAuthExpiryMs(value) {
5125
+ const direct = jwtExpiryMs(value);
5126
+ if (direct !== void 0)
5127
+ return direct;
5128
+ let obj;
5129
+ try {
5130
+ obj = JSON.parse(value);
5131
+ } catch {
5132
+ return void 0;
5133
+ }
5134
+ if (!obj || typeof obj !== "object")
5135
+ return void 0;
5136
+ const record = obj;
5137
+ const nested = record.currentSession ?? record.session;
5138
+ const jwtCandidates = [
5139
+ record.access_token,
5140
+ record.accessToken,
5141
+ record.token,
5142
+ record.id_token,
5143
+ record.idToken,
5144
+ nested?.access_token,
5145
+ nested?.accessToken
5146
+ ];
5147
+ for (const candidate of jwtCandidates) {
5148
+ if (typeof candidate === "string") {
5149
+ const exp = jwtExpiryMs(candidate);
5150
+ if (exp !== void 0)
5151
+ return exp;
5152
+ }
5153
+ }
5154
+ const expiresAt = record.expires_at ?? record.expiresAt ?? nested?.expires_at;
5155
+ if (typeof expiresAt === "number" && Number.isFinite(expiresAt) && expiresAt > 0) {
5156
+ return expiresAt < 1e12 ? expiresAt * 1e3 : expiresAt;
5157
+ }
5158
+ return void 0;
5159
+ }
5160
+ function collectAuthTokens(parsed) {
5161
+ const tokens = [];
5162
+ if (!parsed || typeof parsed !== "object")
5163
+ return tokens;
5164
+ const cookies = parsed.cookies;
5165
+ if (Array.isArray(cookies)) {
5166
+ for (const raw of cookies) {
5167
+ if (!raw || typeof raw !== "object")
5168
+ continue;
5169
+ const name = typeof raw.name === "string" ? raw.name : "";
5170
+ const value = typeof raw.value === "string" ? raw.value : "";
5171
+ if (isCsrfTokenName(name))
5172
+ continue;
5173
+ const jwtExp = value ? jwtExpiryMs(value) : void 0;
5174
+ if (jwtExp !== void 0) {
5175
+ tokens.push({ expiryMs: jwtExp, sessionClass: true });
5176
+ continue;
5177
+ }
5178
+ if (isAuthTokenName(name) && typeof raw.expires === "number" && Number.isFinite(raw.expires) && raw.expires > 0) {
5179
+ tokens.push({ expiryMs: raw.expires * 1e3, sessionClass: isSessionClassName(name) });
5180
+ }
5181
+ }
5182
+ }
5183
+ const origins = parsed.origins;
5184
+ if (Array.isArray(origins)) {
5185
+ for (const origin of origins) {
5186
+ if (!origin || typeof origin !== "object" || !Array.isArray(origin.localStorage))
5187
+ continue;
5188
+ for (const entry of origin.localStorage) {
5189
+ if (!entry || typeof entry !== "object")
5190
+ continue;
5191
+ const key = typeof entry.name === "string" ? entry.name : "";
5192
+ const value = typeof entry.value === "string" ? entry.value : "";
5193
+ if (!key || !value || isCsrfTokenName(key) || !isAuthTokenName(key))
5194
+ continue;
5195
+ const exp = localStorageAuthExpiryMs(value);
5196
+ if (exp !== void 0)
5197
+ tokens.push({ expiryMs: exp, sessionClass: true });
5198
+ }
5199
+ }
5200
+ }
5201
+ return tokens;
5202
+ }
5203
+ function storageStateExpiredBySkew(stateJson, nowMs, skewMs = AUTH_STATE_EXPIRY_SKEW_MS) {
5204
+ let parsed;
5205
+ try {
5206
+ parsed = JSON.parse(stateJson);
5207
+ } catch {
5208
+ return false;
5209
+ }
5210
+ const tokens = collectAuthTokens(parsed);
5211
+ if (tokens.length === 0)
5212
+ return false;
5213
+ const margin = Number.isFinite(skewMs) ? Math.max(0, skewMs) : AUTH_STATE_EXPIRY_SKEW_MS;
5214
+ const deadline = nowMs + margin;
5215
+ const anyExpired = tokens.some((t) => t.expiryMs <= deadline);
5216
+ if (!anyExpired)
5217
+ return false;
5218
+ const liveSession = tokens.some((t) => t.expiryMs > deadline && t.sessionClass);
5219
+ return !liveSession;
5220
+ }
5221
+ var AUTH_STATE_EXPIRY_SKEW_MS;
5222
+ var init_auth_state_freshness = __esm({
5223
+ "../shared/dist/auth-state-freshness.js"() {
5224
+ "use strict";
5225
+ AUTH_STATE_EXPIRY_SKEW_MS = 3e4;
5226
+ }
5227
+ });
5228
+
5044
5229
  // ../shared/dist/skip-coverage-detector.js
5045
5230
  function detectSkippedApiCoverage(steps) {
5046
5231
  const allSkipped = steps.length > 0 && steps.every((s) => s.status === "skipped");
@@ -5430,6 +5615,7 @@ __export(dist_exports, {
5430
5615
  API_AUTH_HEADERS_ENV_KEY: () => API_AUTH_HEADERS_ENV_KEY,
5431
5616
  API_PREFIX: () => API_PREFIX,
5432
5617
  AUDIO_CHUNK_DURATION_MS: () => AUDIO_CHUNK_DURATION_MS,
5618
+ AUTH_STATE_EXPIRY_SKEW_MS: () => AUTH_STATE_EXPIRY_SKEW_MS,
5433
5619
  AUTH_TAG_COOKIE_STATE: () => AUTH_TAG_COOKIE_STATE,
5434
5620
  AUTH_TAG_LOGIN_BEARER: () => AUTH_TAG_LOGIN_BEARER,
5435
5621
  AUTH_TAG_LOGIN_COOKIE: () => AUTH_TAG_LOGIN_COOKIE,
@@ -5547,6 +5733,7 @@ __export(dist_exports, {
5547
5733
  isApiTest: () => isApiTest,
5548
5734
  isAppOriginResolutionEnabled: () => isAppOriginResolutionEnabled,
5549
5735
  isAuthReadinessGuardEnabled: () => isAuthReadinessGuardEnabled,
5736
+ isAuthTokenName: () => isAuthTokenName,
5550
5737
  isBlockedEnvVar: () => isBlockedEnvVar,
5551
5738
  isCountedFailure: () => isCountedFailure,
5552
5739
  isCreditPackAmountCents: () => isCreditPackAmountCents,
@@ -5569,6 +5756,7 @@ __export(dist_exports, {
5569
5756
  isValidSttProvider: () => isValidSttProvider,
5570
5757
  isValidTtsModel: () => isValidTtsModel,
5571
5758
  isValidTtsVoiceId: () => isValidTtsVoiceId,
5759
+ jwtExpiryMs: () => jwtExpiryMs,
5572
5760
  mediumForMobilePlatform: () => mediumForMobilePlatform,
5573
5761
  mobileCredentialPlaceholder: () => mobileCredentialPlaceholder,
5574
5762
  mobilePlatformFromMedium: () => mobilePlatformFromMedium,
@@ -5600,6 +5788,7 @@ __export(dist_exports, {
5600
5788
  stepIsDivergentForPlatform: () => stepIsDivergentForPlatform,
5601
5789
  stepsMissingPlatformLocators: () => stepsMissingPlatformLocators,
5602
5790
  stepsNeedingReviewForPlatform: () => stepsNeedingReviewForPlatform,
5791
+ storageStateExpiredBySkew: () => storageStateExpiredBySkew,
5603
5792
  stripEnvironmentalPrefix: () => stripEnvironmentalPrefix,
5604
5793
  stripLoginScaffolding: () => stripLoginScaffolding,
5605
5794
  sttModelsForProvider: () => sttModelsForProvider,
@@ -5630,6 +5819,7 @@ var init_dist = __esm({
5630
5819
  init_origin_domain();
5631
5820
  init_auth_plan();
5632
5821
  init_auth_block_detector();
5822
+ init_auth_state_freshness();
5633
5823
  init_skip_coverage_detector();
5634
5824
  init_redact_needles();
5635
5825
  init_voice_config();
@@ -333991,7 +334181,7 @@ var require_local_auth_state = __commonJS({
333991
334181
  exports2.ensurePrivateDirectory = ensurePrivateDirectory2;
333992
334182
  exports2.isValidStorageState = isValidStorageState;
333993
334183
  exports2.isValidStorageStateJson = isValidStorageStateJson;
333994
- exports2.isValidLocalAuthStateFile = isValidLocalAuthStateFile;
334184
+ exports2.isValidLocalAuthStateFile = isValidLocalAuthStateFile2;
333995
334185
  exports2.cleanupLocalAuthStateFiles = cleanupLocalAuthStateFiles;
333996
334186
  exports2.cleanupExpiredLocalAuthStateFiles = cleanupExpiredLocalAuthStateFiles2;
333997
334187
  exports2.localAuthStatePaths = localAuthStatePaths2;
@@ -334005,6 +334195,7 @@ var require_local_auth_state = __commonJS({
334005
334195
  var node_fs_1 = require("fs");
334006
334196
  var node_crypto_1 = require("crypto");
334007
334197
  var node_path_1 = require("path");
334198
+ var shared_1 = (init_dist(), __toCommonJS(dist_exports));
334008
334199
  var auth_recipe_js_1 = require_auth_recipe();
334009
334200
  exports2.LOCAL_AUTH_STATE_MAX_AGE_MS = 60 * 60 * 1e3;
334010
334201
  exports2.LOCAL_AUTH_STATE_WAIT_MS = 3 * 60 * 1e3;
@@ -334134,7 +334325,7 @@ var require_local_auth_state = __commonJS({
334134
334325
  return false;
334135
334326
  return isValidStorageState(value);
334136
334327
  }
334137
- function isValidLocalAuthStateFile(path) {
334328
+ function isValidLocalAuthStateFile2(path) {
334138
334329
  try {
334139
334330
  return isStorageStateShape(JSON.parse((0, node_fs_1.readFileSync)(path, "utf8")));
334140
334331
  } catch {
@@ -334192,11 +334383,22 @@ var require_local_auth_state = __commonJS({
334192
334383
  lockPath: `${cachePath}.lock`
334193
334384
  };
334194
334385
  }
334386
+ function localAuthStateExpiredByAuthMaterial(path, now) {
334387
+ try {
334388
+ return (0, shared_1.storageStateExpiredBySkew)((0, node_fs_1.readFileSync)(path, "utf8"), now);
334389
+ } catch {
334390
+ return false;
334391
+ }
334392
+ }
334195
334393
  function getValidLocalAuthState2(path, now = Date.now(), maxAgeMs = exports2.LOCAL_AUTH_STATE_MAX_AGE_MS) {
334196
334394
  if (!(0, node_fs_1.existsSync)(path))
334197
334395
  return void 0;
334198
334396
  try {
334199
- if (now - (0, node_fs_1.statSync)(path).mtimeMs >= maxAgeMs || !isValidLocalAuthStateFile(path)) {
334397
+ if (now - (0, node_fs_1.statSync)(path).mtimeMs >= maxAgeMs || !isValidLocalAuthStateFile2(path)) {
334398
+ removeFile(path);
334399
+ return void 0;
334400
+ }
334401
+ if (localAuthStateExpiredByAuthMaterial(path, now)) {
334200
334402
  removeFile(path);
334201
334403
  return void 0;
334202
334404
  }
@@ -334243,7 +334445,7 @@ var require_local_auth_state = __commonJS({
334243
334445
  removeFile(capturePath);
334244
334446
  return false;
334245
334447
  }
334246
- if (!(0, node_fs_1.existsSync)(capturePath) || !isValidLocalAuthStateFile(capturePath)) {
334448
+ if (!(0, node_fs_1.existsSync)(capturePath) || !isValidLocalAuthStateFile2(capturePath)) {
334247
334449
  removeFile(capturePath);
334248
334450
  return false;
334249
334451
  }
@@ -334257,7 +334459,7 @@ var require_local_auth_state = __commonJS({
334257
334459
  return false;
334258
334460
  }
334259
334461
  if ((0, node_fs_1.existsSync)(cachePath)) {
334260
- if (!replaceExisting && isValidLocalAuthStateFile(cachePath)) {
334462
+ if (!replaceExisting && isValidLocalAuthStateFile2(cachePath)) {
334261
334463
  removeFile(capturePath);
334262
334464
  return true;
334263
334465
  }
@@ -334274,7 +334476,7 @@ var require_local_auth_state = __commonJS({
334274
334476
  }
334275
334477
  return true;
334276
334478
  } catch {
334277
- const winnerIsValid = (0, node_fs_1.existsSync)(cachePath) && isValidLocalAuthStateFile(cachePath);
334479
+ const winnerIsValid = (0, node_fs_1.existsSync)(cachePath) && isValidLocalAuthStateFile2(cachePath);
334278
334480
  removeFile(capturePath);
334279
334481
  return winnerIsValid;
334280
334482
  }
@@ -346967,7 +347169,7 @@ var require_package4 = __commonJS({
346967
347169
  "package.json"(exports2, module2) {
346968
347170
  module2.exports = {
346969
347171
  name: "@validate.qa/runner",
346970
- version: "1.0.47",
347172
+ version: "1.0.48",
346971
347173
  description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
346972
347174
  bin: {
346973
347175
  "validate-runner": "dist/cli.js"
@@ -356629,7 +356831,7 @@ ${result2.logs}`);
356629
356831
  });
356630
356832
  } finally {
356631
356833
  if (runAuthPath && authPaths) {
356632
- if ((0, import_runner_core12.getValidLocalAuthState)(runAuthPath)) {
356834
+ if ((0, import_runner_core12.isValidLocalAuthStateFile)(runAuthPath)) {
356633
356835
  const releasePromotionLock = releaseAuthLock ? void 0 : await (0, import_runner_core12.acquireLocalAuthStateLock)(authPaths.lockKey, authPaths.lockPath);
356634
356836
  try {
356635
356837
  const captured = (0, import_runner_core12.promoteLocalAuthStateCapture)(
package/dist/cli.mjs CHANGED
@@ -5046,6 +5046,191 @@ var init_auth_block_detector = __esm({
5046
5046
  }
5047
5047
  });
5048
5048
 
5049
+ // ../shared/dist/auth-state-freshness.js
5050
+ function isCsrfTokenName(name) {
5051
+ return /csrf|xsrf|forgery|authenticity|requestverification/.test(name.toLowerCase());
5052
+ }
5053
+ function isAuthTokenName(name) {
5054
+ const n = name.toLowerCase();
5055
+ if (isCsrfTokenName(n))
5056
+ return false;
5057
+ if (/session|auth|jwt|credential|identity|login|token/.test(n))
5058
+ return true;
5059
+ if (/(?:^|[^a-z0-9])(?:sid|sess|account|remember)(?:[^a-z0-9]|$)/.test(n))
5060
+ return true;
5061
+ return [
5062
+ "connect.sid",
5063
+ "jsessionid",
5064
+ "phpsessid",
5065
+ "aspnet",
5066
+ "asp.net",
5067
+ "aspxauth",
5068
+ "sb-",
5069
+ "supabase",
5070
+ "amplify",
5071
+ "cognito",
5072
+ "auth0",
5073
+ "okta",
5074
+ "kc-",
5075
+ "li_at",
5076
+ "ltpatoken",
5077
+ "wordpress_logged_in"
5078
+ ].some((frag) => n.includes(frag));
5079
+ }
5080
+ function isSessionClassName(name) {
5081
+ const n = name.toLowerCase();
5082
+ if (isCsrfTokenName(n))
5083
+ return false;
5084
+ if (/session|sess/.test(n))
5085
+ return true;
5086
+ if (/(?:^|[^a-z0-9])sid(?:[^a-z0-9]|$)/.test(n))
5087
+ return true;
5088
+ return [
5089
+ "connect.sid",
5090
+ "jsessionid",
5091
+ "phpsessid",
5092
+ "aspxauth",
5093
+ "li_at",
5094
+ "ltpatoken",
5095
+ "wordpress_logged_in"
5096
+ ].some((frag) => n.includes(frag));
5097
+ }
5098
+ function decodeBase64UrlToUtf8(segment) {
5099
+ if (typeof atob !== "function")
5100
+ return void 0;
5101
+ const base64 = segment.replace(/-/g, "+").replace(/_/g, "/");
5102
+ const padded = base64.length % 4 === 0 ? base64 : base64 + "=".repeat(4 - base64.length % 4);
5103
+ try {
5104
+ const binary = atob(padded);
5105
+ let percentEncoded = "";
5106
+ for (let i = 0; i < binary.length; i += 1) {
5107
+ percentEncoded += `%${binary.charCodeAt(i).toString(16).padStart(2, "0")}`;
5108
+ }
5109
+ return decodeURIComponent(percentEncoded);
5110
+ } catch {
5111
+ return void 0;
5112
+ }
5113
+ }
5114
+ function jwtExpiryMs(value) {
5115
+ const parts = value.split(".");
5116
+ if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2])
5117
+ return void 0;
5118
+ const json = decodeBase64UrlToUtf8(parts[1]);
5119
+ if (json === void 0)
5120
+ return void 0;
5121
+ try {
5122
+ const payload = JSON.parse(json);
5123
+ const exp = payload?.exp;
5124
+ return typeof exp === "number" && Number.isFinite(exp) && exp > 0 ? exp * 1e3 : void 0;
5125
+ } catch {
5126
+ return void 0;
5127
+ }
5128
+ }
5129
+ function localStorageAuthExpiryMs(value) {
5130
+ const direct = jwtExpiryMs(value);
5131
+ if (direct !== void 0)
5132
+ return direct;
5133
+ let obj;
5134
+ try {
5135
+ obj = JSON.parse(value);
5136
+ } catch {
5137
+ return void 0;
5138
+ }
5139
+ if (!obj || typeof obj !== "object")
5140
+ return void 0;
5141
+ const record = obj;
5142
+ const nested = record.currentSession ?? record.session;
5143
+ const jwtCandidates = [
5144
+ record.access_token,
5145
+ record.accessToken,
5146
+ record.token,
5147
+ record.id_token,
5148
+ record.idToken,
5149
+ nested?.access_token,
5150
+ nested?.accessToken
5151
+ ];
5152
+ for (const candidate of jwtCandidates) {
5153
+ if (typeof candidate === "string") {
5154
+ const exp = jwtExpiryMs(candidate);
5155
+ if (exp !== void 0)
5156
+ return exp;
5157
+ }
5158
+ }
5159
+ const expiresAt = record.expires_at ?? record.expiresAt ?? nested?.expires_at;
5160
+ if (typeof expiresAt === "number" && Number.isFinite(expiresAt) && expiresAt > 0) {
5161
+ return expiresAt < 1e12 ? expiresAt * 1e3 : expiresAt;
5162
+ }
5163
+ return void 0;
5164
+ }
5165
+ function collectAuthTokens(parsed) {
5166
+ const tokens = [];
5167
+ if (!parsed || typeof parsed !== "object")
5168
+ return tokens;
5169
+ const cookies = parsed.cookies;
5170
+ if (Array.isArray(cookies)) {
5171
+ for (const raw of cookies) {
5172
+ if (!raw || typeof raw !== "object")
5173
+ continue;
5174
+ const name = typeof raw.name === "string" ? raw.name : "";
5175
+ const value = typeof raw.value === "string" ? raw.value : "";
5176
+ if (isCsrfTokenName(name))
5177
+ continue;
5178
+ const jwtExp = value ? jwtExpiryMs(value) : void 0;
5179
+ if (jwtExp !== void 0) {
5180
+ tokens.push({ expiryMs: jwtExp, sessionClass: true });
5181
+ continue;
5182
+ }
5183
+ if (isAuthTokenName(name) && typeof raw.expires === "number" && Number.isFinite(raw.expires) && raw.expires > 0) {
5184
+ tokens.push({ expiryMs: raw.expires * 1e3, sessionClass: isSessionClassName(name) });
5185
+ }
5186
+ }
5187
+ }
5188
+ const origins = parsed.origins;
5189
+ if (Array.isArray(origins)) {
5190
+ for (const origin of origins) {
5191
+ if (!origin || typeof origin !== "object" || !Array.isArray(origin.localStorage))
5192
+ continue;
5193
+ for (const entry of origin.localStorage) {
5194
+ if (!entry || typeof entry !== "object")
5195
+ continue;
5196
+ const key = typeof entry.name === "string" ? entry.name : "";
5197
+ const value = typeof entry.value === "string" ? entry.value : "";
5198
+ if (!key || !value || isCsrfTokenName(key) || !isAuthTokenName(key))
5199
+ continue;
5200
+ const exp = localStorageAuthExpiryMs(value);
5201
+ if (exp !== void 0)
5202
+ tokens.push({ expiryMs: exp, sessionClass: true });
5203
+ }
5204
+ }
5205
+ }
5206
+ return tokens;
5207
+ }
5208
+ function storageStateExpiredBySkew(stateJson, nowMs, skewMs = AUTH_STATE_EXPIRY_SKEW_MS) {
5209
+ let parsed;
5210
+ try {
5211
+ parsed = JSON.parse(stateJson);
5212
+ } catch {
5213
+ return false;
5214
+ }
5215
+ const tokens = collectAuthTokens(parsed);
5216
+ if (tokens.length === 0)
5217
+ return false;
5218
+ const margin = Number.isFinite(skewMs) ? Math.max(0, skewMs) : AUTH_STATE_EXPIRY_SKEW_MS;
5219
+ const deadline = nowMs + margin;
5220
+ const anyExpired = tokens.some((t) => t.expiryMs <= deadline);
5221
+ if (!anyExpired)
5222
+ return false;
5223
+ const liveSession = tokens.some((t) => t.expiryMs > deadline && t.sessionClass);
5224
+ return !liveSession;
5225
+ }
5226
+ var AUTH_STATE_EXPIRY_SKEW_MS;
5227
+ var init_auth_state_freshness = __esm({
5228
+ "../shared/dist/auth-state-freshness.js"() {
5229
+ "use strict";
5230
+ AUTH_STATE_EXPIRY_SKEW_MS = 3e4;
5231
+ }
5232
+ });
5233
+
5049
5234
  // ../shared/dist/skip-coverage-detector.js
5050
5235
  function detectSkippedApiCoverage(steps) {
5051
5236
  const allSkipped = steps.length > 0 && steps.every((s) => s.status === "skipped");
@@ -5435,6 +5620,7 @@ __export(dist_exports, {
5435
5620
  API_AUTH_HEADERS_ENV_KEY: () => API_AUTH_HEADERS_ENV_KEY,
5436
5621
  API_PREFIX: () => API_PREFIX,
5437
5622
  AUDIO_CHUNK_DURATION_MS: () => AUDIO_CHUNK_DURATION_MS,
5623
+ AUTH_STATE_EXPIRY_SKEW_MS: () => AUTH_STATE_EXPIRY_SKEW_MS,
5438
5624
  AUTH_TAG_COOKIE_STATE: () => AUTH_TAG_COOKIE_STATE,
5439
5625
  AUTH_TAG_LOGIN_BEARER: () => AUTH_TAG_LOGIN_BEARER,
5440
5626
  AUTH_TAG_LOGIN_COOKIE: () => AUTH_TAG_LOGIN_COOKIE,
@@ -5552,6 +5738,7 @@ __export(dist_exports, {
5552
5738
  isApiTest: () => isApiTest,
5553
5739
  isAppOriginResolutionEnabled: () => isAppOriginResolutionEnabled,
5554
5740
  isAuthReadinessGuardEnabled: () => isAuthReadinessGuardEnabled,
5741
+ isAuthTokenName: () => isAuthTokenName,
5555
5742
  isBlockedEnvVar: () => isBlockedEnvVar,
5556
5743
  isCountedFailure: () => isCountedFailure,
5557
5744
  isCreditPackAmountCents: () => isCreditPackAmountCents,
@@ -5574,6 +5761,7 @@ __export(dist_exports, {
5574
5761
  isValidSttProvider: () => isValidSttProvider,
5575
5762
  isValidTtsModel: () => isValidTtsModel,
5576
5763
  isValidTtsVoiceId: () => isValidTtsVoiceId,
5764
+ jwtExpiryMs: () => jwtExpiryMs,
5577
5765
  mediumForMobilePlatform: () => mediumForMobilePlatform,
5578
5766
  mobileCredentialPlaceholder: () => mobileCredentialPlaceholder,
5579
5767
  mobilePlatformFromMedium: () => mobilePlatformFromMedium,
@@ -5605,6 +5793,7 @@ __export(dist_exports, {
5605
5793
  stepIsDivergentForPlatform: () => stepIsDivergentForPlatform,
5606
5794
  stepsMissingPlatformLocators: () => stepsMissingPlatformLocators,
5607
5795
  stepsNeedingReviewForPlatform: () => stepsNeedingReviewForPlatform,
5796
+ storageStateExpiredBySkew: () => storageStateExpiredBySkew,
5608
5797
  stripEnvironmentalPrefix: () => stripEnvironmentalPrefix,
5609
5798
  stripLoginScaffolding: () => stripLoginScaffolding,
5610
5799
  sttModelsForProvider: () => sttModelsForProvider,
@@ -5635,6 +5824,7 @@ var init_dist = __esm({
5635
5824
  init_origin_domain();
5636
5825
  init_auth_plan();
5637
5826
  init_auth_block_detector();
5827
+ init_auth_state_freshness();
5638
5828
  init_skip_coverage_detector();
5639
5829
  init_redact_needles();
5640
5830
  init_voice_config();
@@ -333996,7 +334186,7 @@ var require_local_auth_state = __commonJS({
333996
334186
  exports.ensurePrivateDirectory = ensurePrivateDirectory2;
333997
334187
  exports.isValidStorageState = isValidStorageState;
333998
334188
  exports.isValidStorageStateJson = isValidStorageStateJson;
333999
- exports.isValidLocalAuthStateFile = isValidLocalAuthStateFile;
334189
+ exports.isValidLocalAuthStateFile = isValidLocalAuthStateFile2;
334000
334190
  exports.cleanupLocalAuthStateFiles = cleanupLocalAuthStateFiles;
334001
334191
  exports.cleanupExpiredLocalAuthStateFiles = cleanupExpiredLocalAuthStateFiles2;
334002
334192
  exports.localAuthStatePaths = localAuthStatePaths2;
@@ -334010,6 +334200,7 @@ var require_local_auth_state = __commonJS({
334010
334200
  var node_fs_1 = __require("fs");
334011
334201
  var node_crypto_1 = __require("crypto");
334012
334202
  var node_path_1 = __require("path");
334203
+ var shared_1 = (init_dist(), __toCommonJS(dist_exports));
334013
334204
  var auth_recipe_js_1 = require_auth_recipe();
334014
334205
  exports.LOCAL_AUTH_STATE_MAX_AGE_MS = 60 * 60 * 1e3;
334015
334206
  exports.LOCAL_AUTH_STATE_WAIT_MS = 3 * 60 * 1e3;
@@ -334139,7 +334330,7 @@ var require_local_auth_state = __commonJS({
334139
334330
  return false;
334140
334331
  return isValidStorageState(value);
334141
334332
  }
334142
- function isValidLocalAuthStateFile(path) {
334333
+ function isValidLocalAuthStateFile2(path) {
334143
334334
  try {
334144
334335
  return isStorageStateShape(JSON.parse((0, node_fs_1.readFileSync)(path, "utf8")));
334145
334336
  } catch {
@@ -334197,11 +334388,22 @@ var require_local_auth_state = __commonJS({
334197
334388
  lockPath: `${cachePath}.lock`
334198
334389
  };
334199
334390
  }
334391
+ function localAuthStateExpiredByAuthMaterial(path, now) {
334392
+ try {
334393
+ return (0, shared_1.storageStateExpiredBySkew)((0, node_fs_1.readFileSync)(path, "utf8"), now);
334394
+ } catch {
334395
+ return false;
334396
+ }
334397
+ }
334200
334398
  function getValidLocalAuthState2(path, now = Date.now(), maxAgeMs = exports.LOCAL_AUTH_STATE_MAX_AGE_MS) {
334201
334399
  if (!(0, node_fs_1.existsSync)(path))
334202
334400
  return void 0;
334203
334401
  try {
334204
- if (now - (0, node_fs_1.statSync)(path).mtimeMs >= maxAgeMs || !isValidLocalAuthStateFile(path)) {
334402
+ if (now - (0, node_fs_1.statSync)(path).mtimeMs >= maxAgeMs || !isValidLocalAuthStateFile2(path)) {
334403
+ removeFile(path);
334404
+ return void 0;
334405
+ }
334406
+ if (localAuthStateExpiredByAuthMaterial(path, now)) {
334205
334407
  removeFile(path);
334206
334408
  return void 0;
334207
334409
  }
@@ -334248,7 +334450,7 @@ var require_local_auth_state = __commonJS({
334248
334450
  removeFile(capturePath);
334249
334451
  return false;
334250
334452
  }
334251
- if (!(0, node_fs_1.existsSync)(capturePath) || !isValidLocalAuthStateFile(capturePath)) {
334453
+ if (!(0, node_fs_1.existsSync)(capturePath) || !isValidLocalAuthStateFile2(capturePath)) {
334252
334454
  removeFile(capturePath);
334253
334455
  return false;
334254
334456
  }
@@ -334262,7 +334464,7 @@ var require_local_auth_state = __commonJS({
334262
334464
  return false;
334263
334465
  }
334264
334466
  if ((0, node_fs_1.existsSync)(cachePath)) {
334265
- if (!replaceExisting && isValidLocalAuthStateFile(cachePath)) {
334467
+ if (!replaceExisting && isValidLocalAuthStateFile2(cachePath)) {
334266
334468
  removeFile(capturePath);
334267
334469
  return true;
334268
334470
  }
@@ -334279,7 +334481,7 @@ var require_local_auth_state = __commonJS({
334279
334481
  }
334280
334482
  return true;
334281
334483
  } catch {
334282
- const winnerIsValid = (0, node_fs_1.existsSync)(cachePath) && isValidLocalAuthStateFile(cachePath);
334484
+ const winnerIsValid = (0, node_fs_1.existsSync)(cachePath) && isValidLocalAuthStateFile2(cachePath);
334283
334485
  removeFile(capturePath);
334284
334486
  return winnerIsValid;
334285
334487
  }
@@ -346972,7 +347174,7 @@ var require_package4 = __commonJS({
346972
347174
  "package.json"(exports, module) {
346973
347175
  module.exports = {
346974
347176
  name: "@validate.qa/runner",
346975
- version: "1.0.47",
347177
+ version: "1.0.48",
346976
347178
  description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
346977
347179
  bin: {
346978
347180
  "validate-runner": "dist/cli.js"
@@ -356637,7 +356839,7 @@ ${result2.logs}`);
356637
356839
  });
356638
356840
  } finally {
356639
356841
  if (runAuthPath && authPaths) {
356640
- if ((0, import_runner_core12.getValidLocalAuthState)(runAuthPath)) {
356842
+ if ((0, import_runner_core12.isValidLocalAuthStateFile)(runAuthPath)) {
356641
356843
  const releasePromotionLock = releaseAuthLock ? void 0 : await (0, import_runner_core12.acquireLocalAuthStateLock)(authPaths.lockKey, authPaths.lockPath);
356642
356844
  try {
356643
356845
  const captured = (0, import_runner_core12.promoteLocalAuthStateCapture)(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@validate.qa/runner",
3
- "version": "1.0.47",
3
+ "version": "1.0.48",
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"