@hasna/testers 0.0.45 → 0.0.47

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/index.js CHANGED
@@ -59,6 +59,12 @@ function workflowExecutionFromValue(value) {
59
59
  const sandboxSyncStrategy = syncStrategyValue(input["sandboxSyncStrategy"]);
60
60
  const setupCommand = stringValue(input["setupCommand"]);
61
61
  const packageSpec = stringValue(input["packageSpec"]);
62
+ const appSourceDir = stringValue(input["appSourceDir"]);
63
+ const appRemoteDir = stringValue(input["appRemoteDir"]);
64
+ const appStartCommand = stringValue(input["appStartCommand"]);
65
+ const appUrl = stringValue(input["appUrl"]);
66
+ const appWaitUrl = stringValue(input["appWaitUrl"]);
67
+ const appWaitTimeoutMs = numberValue(input["appWaitTimeoutMs"]);
62
68
  const timeoutMs = numberValue(input["timeoutMs"]);
63
69
  const env = stringMap(input["env"]);
64
70
  return {
@@ -70,6 +76,12 @@ function workflowExecutionFromValue(value) {
70
76
  ...sandboxSyncStrategy ? { sandboxSyncStrategy } : {},
71
77
  ...setupCommand ? { setupCommand } : {},
72
78
  ...packageSpec ? { packageSpec } : {},
79
+ ...appSourceDir ? { appSourceDir } : {},
80
+ ...appRemoteDir ? { appRemoteDir } : {},
81
+ ...appStartCommand ? { appStartCommand } : {},
82
+ ...appUrl ? { appUrl } : {},
83
+ ...appWaitUrl ? { appWaitUrl } : {},
84
+ ...appWaitTimeoutMs !== undefined ? { appWaitTimeoutMs } : {},
73
85
  ...timeoutMs !== undefined ? { timeoutMs } : {},
74
86
  ...env ? { env } : {}
75
87
  };
@@ -17200,9 +17212,20 @@ function estimateCost(model, tokens) {
17200
17212
  }
17201
17213
  // src/lib/workflow-runner.ts
17202
17214
  init_database();
17203
- import { mkdtempSync, rmSync, writeFileSync as writeFileSync3 } from "fs";
17215
+ import { spawnSync } from "child_process";
17216
+ import { existsSync as existsSync11, mkdirSync as mkdirSync9, mkdtempSync, rmSync, statSync } from "fs";
17204
17217
  import { tmpdir } from "os";
17205
- import { join as join14 } from "path";
17218
+ import { join as join14, posix as pathPosix } from "path";
17219
+ var APP_SOURCE_EXCLUDES = [
17220
+ "node_modules",
17221
+ ".git",
17222
+ "dist",
17223
+ ".next",
17224
+ ".turbo",
17225
+ ".cache",
17226
+ ".venv",
17227
+ "__pycache__"
17228
+ ];
17206
17229
  function buildWorkflowRunPlan(workflow, options) {
17207
17230
  const runOptions = {
17208
17231
  url: options.url,
@@ -17244,10 +17267,16 @@ function createWorkflowDatabaseBundle(workflow, plan) {
17244
17267
  if (!plan.sandbox)
17245
17268
  throw new Error(`Workflow is not configured for sandbox execution: ${workflow.name}`);
17246
17269
  const localDir = mkdtempSync(join14(tmpdir(), `testers-workflow-${workflow.id.slice(0, 8)}-`));
17247
- writeFileSync3(join14(localDir, "testers.db"), getDatabase().serialize());
17270
+ const stateDir = join14(localDir, ".testers-state");
17271
+ mkdirSync9(stateDir, { recursive: true });
17272
+ writeDatabaseSnapshot(join14(stateDir, "testers.db"));
17273
+ if (plan.sandbox.appSourceDir && plan.sandbox.appRemoteDir) {
17274
+ const relativeAppDir = relativeRemotePath(plan.sandbox.remoteDir, plan.sandbox.appRemoteDir);
17275
+ copyAppSource(plan.sandbox.appSourceDir, join14(localDir, relativeAppDir));
17276
+ }
17248
17277
  return {
17249
17278
  localDir,
17250
- remoteDir: plan.sandbox.stateRemoteDir,
17279
+ remoteDir: plan.sandbox.remoteDir,
17251
17280
  cleanup: () => rmSync(localDir, { recursive: true, force: true })
17252
17281
  };
17253
17282
  }
@@ -17258,15 +17287,63 @@ function validatePersonaIds(workflow) {
17258
17287
  }
17259
17288
  }
17260
17289
  }
17290
+ function relativeRemotePath(remoteDir, remoteChildDir) {
17291
+ if (!remoteChildDir.startsWith("/")) {
17292
+ const relative3 = remoteChildDir.replace(/^\/+/, "").replace(/\/+$/, "");
17293
+ if (!relative3 || relative3 === ".") {
17294
+ throw new Error("Sandbox app remote directory must be a child directory, not the workflow root");
17295
+ }
17296
+ return relative3;
17297
+ }
17298
+ const base = remoteDir.replace(/\/+$/, "") || "/";
17299
+ const child = remoteChildDir.replace(/\/+$/, "") || "/";
17300
+ const relative2 = pathPosix.relative(base, child);
17301
+ if (!relative2 || relative2 === "." || relative2.startsWith("..") || pathPosix.isAbsolute(relative2)) {
17302
+ throw new Error(`Sandbox app remote directory must be inside the workflow remote directory (${remoteDir}): ${remoteChildDir}`);
17303
+ }
17304
+ return relative2;
17305
+ }
17306
+ function copyAppSource(sourceDir, targetDir) {
17307
+ if (!existsSync11(sourceDir) || !statSync(sourceDir).isDirectory()) {
17308
+ throw new Error(`Sandbox app source directory does not exist or is not a directory: ${sourceDir}`);
17309
+ }
17310
+ mkdirSync9(targetDir, { recursive: true });
17311
+ const result = spawnSync("rsync", [
17312
+ "-a",
17313
+ "--delete",
17314
+ ...APP_SOURCE_EXCLUDES.flatMap((item) => ["--exclude", item]),
17315
+ `${sourceDir.replace(/\/+$/, "")}/`,
17316
+ `${targetDir.replace(/\/+$/, "")}/`
17317
+ ], { encoding: "utf8" });
17318
+ if (result.error) {
17319
+ throw new Error(`Failed to rsync sandbox app source: ${result.error.message}`);
17320
+ }
17321
+ if (result.status !== 0) {
17322
+ throw new Error(`Failed to rsync sandbox app source (${result.status}): ${result.stderr.trim()}`);
17323
+ }
17324
+ }
17325
+ function writeDatabaseSnapshot(targetPath) {
17326
+ getDatabase().exec(`VACUUM INTO ${sqlString(targetPath)}`);
17327
+ }
17328
+ function sqlString(value) {
17329
+ return `'${value.replaceAll("'", "''")}'`;
17330
+ }
17261
17331
  function buildSandboxPlan(workflow, execution, runOptions) {
17262
17332
  const remoteDir = execution.sandboxRemoteDir ?? `/tmp/testers-workflow-${workflow.id.slice(0, 8)}`;
17263
17333
  const stateRemoteDir = `${remoteDir.replace(/\/+$/, "")}/.testers-state`;
17334
+ const appRemoteDir = execution.appSourceDir ? execution.appRemoteDir ?? `${remoteDir.replace(/\/+$/, "")}/app` : execution.appRemoteDir;
17264
17335
  return {
17265
17336
  provider: execution.provider,
17266
17337
  image: execution.sandboxImage,
17267
17338
  name: `testers-${workflow.id.slice(0, 8)}`,
17268
17339
  remoteDir,
17269
17340
  stateRemoteDir,
17341
+ ...execution.appSourceDir ? { appSourceDir: execution.appSourceDir } : {},
17342
+ ...appRemoteDir ? { appRemoteDir } : {},
17343
+ ...execution.appStartCommand ? { appStartCommand: execution.appStartCommand } : {},
17344
+ ...execution.appUrl ? { appUrl: execution.appUrl } : {},
17345
+ ...execution.appWaitUrl ? { appWaitUrl: execution.appWaitUrl } : {},
17346
+ ...execution.appWaitTimeoutMs !== undefined ? { appWaitTimeoutMs: execution.appWaitTimeoutMs } : {},
17270
17347
  cleanup: execution.sandboxCleanup ?? "delete",
17271
17348
  syncStrategy: execution.sandboxSyncStrategy ?? "rsync",
17272
17349
  timeoutMs: execution.timeoutMs,
@@ -17274,6 +17351,12 @@ function buildSandboxPlan(workflow, execution, runOptions) {
17274
17351
  command: buildSandboxCommand({
17275
17352
  runOptions,
17276
17353
  remoteDir,
17354
+ stateRemoteDir,
17355
+ appRemoteDir,
17356
+ appStartCommand: execution.appStartCommand,
17357
+ appUrl: execution.appUrl,
17358
+ appWaitUrl: execution.appWaitUrl,
17359
+ appWaitTimeoutMs: execution.appWaitTimeoutMs,
17277
17360
  dbPath: `${stateRemoteDir}/testers.db`,
17278
17361
  setupCommand: execution.setupCommand,
17279
17362
  packageSpec: execution.packageSpec ?? "@hasna/testers"
@@ -17281,11 +17364,12 @@ function buildSandboxPlan(workflow, execution, runOptions) {
17281
17364
  };
17282
17365
  }
17283
17366
  function buildSandboxCommand(input) {
17367
+ const targetUrl = input.appUrl ?? input.runOptions.url;
17284
17368
  const args = [
17285
17369
  "bunx",
17286
17370
  input.packageSpec,
17287
17371
  "run",
17288
- input.runOptions.url,
17372
+ targetUrl,
17289
17373
  ...input.runOptions.scenarioIds?.length ? ["--scenario", input.runOptions.scenarioIds.join(",")] : [],
17290
17374
  ...input.runOptions.tags?.length ? input.runOptions.tags.flatMap((tag) => ["--tag", tag]) : [],
17291
17375
  ...input.runOptions.priority ? ["--priority", input.runOptions.priority] : [],
@@ -17301,12 +17385,46 @@ function buildSandboxCommand(input) {
17301
17385
  return [
17302
17386
  "set -euo pipefail",
17303
17387
  `mkdir -p ${shellQuote(input.remoteDir)}`,
17304
- `cd ${shellQuote(input.remoteDir)}`,
17388
+ `mkdir -p ${shellQuote(input.stateRemoteDir)}`,
17389
+ input.appRemoteDir ? `mkdir -p ${shellQuote(input.appRemoteDir)}` : undefined,
17390
+ `cd ${shellQuote(input.appRemoteDir ?? input.remoteDir)}`,
17305
17391
  input.setupCommand,
17392
+ buildAppStartCommand(input),
17306
17393
  `HASNA_TESTERS_DB_PATH=${shellQuote(input.dbPath)} ${args.map(shellQuote).join(" ")}`
17307
17394
  ].filter(Boolean).join(`
17308
17395
  `);
17309
17396
  }
17397
+ function buildAppStartCommand(input) {
17398
+ if (!input.appStartCommand)
17399
+ return;
17400
+ const waitUrl = input.appWaitUrl ?? input.appUrl ?? input.runOptions.url;
17401
+ const waitTimeoutMs = input.appWaitTimeoutMs ?? 120000;
17402
+ return [
17403
+ `( ${input.appStartCommand} ) > ${shellQuote(`${input.stateRemoteDir}/app.log`)} 2>&1 &`,
17404
+ "APP_PID=$!",
17405
+ `echo "$APP_PID" > ${shellQuote(`${input.stateRemoteDir}/app.pid`)}`,
17406
+ `trap 'kill "$APP_PID" 2>/dev/null || true' EXIT`,
17407
+ waitUrl ? buildWaitForUrlCommand(waitUrl, waitTimeoutMs) : undefined
17408
+ ].filter(Boolean).join(`
17409
+ `);
17410
+ }
17411
+ function buildWaitForUrlCommand(url, timeoutMs) {
17412
+ const script = `
17413
+ const url = ${JSON.stringify(url)};
17414
+ const timeoutMs = ${JSON.stringify(timeoutMs)};
17415
+ const deadline = Date.now() + timeoutMs;
17416
+ while (Date.now() <= deadline) {
17417
+ try {
17418
+ const response = await fetch(url);
17419
+ if (response.status >= 200 && response.status < 500) process.exit(0);
17420
+ } catch {}
17421
+ await new Promise((resolve) => setTimeout(resolve, 1000));
17422
+ }
17423
+ console.error(\`Timed out waiting for \${url} after \${timeoutMs}ms\`);
17424
+ process.exit(1);
17425
+ `.trim();
17426
+ return `bun -e ${shellQuote(script)}`;
17427
+ }
17310
17428
  async function runViaSandbox(plan, dependencies) {
17311
17429
  if (!plan.sandbox)
17312
17430
  throw new Error("Workflow does not have a sandbox plan");
@@ -17327,7 +17445,7 @@ async function runViaSandbox(plan, dependencies) {
17327
17445
  workflowId: plan.workflow.id,
17328
17446
  workflowName: plan.workflow.name
17329
17447
  },
17330
- sandboxEnvVars: plan.sandbox.env,
17448
+ sandboxEnvVars: resolveSandboxEnv(plan.sandbox.env),
17331
17449
  cleanup: plan.sandbox.cleanup,
17332
17450
  upload: {
17333
17451
  localDir: bundle.localDir,
@@ -17353,6 +17471,19 @@ async function runViaSandbox(plan, dependencies) {
17353
17471
  bundle.cleanup?.();
17354
17472
  }
17355
17473
  }
17474
+ function resolveSandboxEnv(env2) {
17475
+ if (!env2 || Object.keys(env2).length === 0)
17476
+ return;
17477
+ const resolved = {};
17478
+ for (const [key, value] of Object.entries(env2)) {
17479
+ const resolvedValue = resolveCredential(value);
17480
+ if (resolvedValue === null) {
17481
+ throw new Error(`Missing sandbox env value for ${key}`);
17482
+ }
17483
+ resolved[key] = resolvedValue;
17484
+ }
17485
+ return resolved;
17486
+ }
17356
17487
  async function resolveSandboxesRuntime(dependencies) {
17357
17488
  if (dependencies.sandboxes)
17358
17489
  return dependencies.sandboxes;
@@ -17829,11 +17960,11 @@ class Scheduler {
17829
17960
  }
17830
17961
  // src/lib/init.ts
17831
17962
  init_paths();
17832
- import { existsSync as existsSync11, readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync9 } from "fs";
17963
+ import { existsSync as existsSync12, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync10 } from "fs";
17833
17964
  import { join as join15, basename } from "path";
17834
17965
  function detectFramework(dir) {
17835
17966
  const pkgPath = join15(dir, "package.json");
17836
- if (!existsSync11(pkgPath))
17967
+ if (!existsSync12(pkgPath))
17837
17968
  return null;
17838
17969
  let pkg;
17839
17970
  try {
@@ -18061,17 +18192,17 @@ function initProject(options) {
18061
18192
  }).filter((s) => s !== null);
18062
18193
  const configDir = getTestersDir();
18063
18194
  const configPath = join15(configDir, "config.json");
18064
- if (!existsSync11(configDir)) {
18065
- mkdirSync9(configDir, { recursive: true });
18195
+ if (!existsSync12(configDir)) {
18196
+ mkdirSync10(configDir, { recursive: true });
18066
18197
  }
18067
18198
  let config = {};
18068
- if (existsSync11(configPath)) {
18199
+ if (existsSync12(configPath)) {
18069
18200
  try {
18070
18201
  config = JSON.parse(readFileSync3(configPath, "utf-8"));
18071
18202
  } catch {}
18072
18203
  }
18073
18204
  config.activeProject = project.id;
18074
- writeFileSync4(configPath, JSON.stringify(config, null, 2), "utf-8");
18205
+ writeFileSync3(configPath, JSON.stringify(config, null, 2), "utf-8");
18075
18206
  return { project, scenarios, framework, url };
18076
18207
  }
18077
18208
  // src/lib/smoke.ts
@@ -19701,9 +19832,9 @@ function deleteAuthPreset(name) {
19701
19832
  }
19702
19833
  // src/lib/report.ts
19703
19834
  init_runs();
19704
- import { readFileSync as readFileSync4, existsSync as existsSync12 } from "fs";
19835
+ import { readFileSync as readFileSync4, existsSync as existsSync13 } from "fs";
19705
19836
  function imageToBase64(filePath) {
19706
- if (!filePath || !existsSync12(filePath))
19837
+ if (!filePath || !existsSync13(filePath))
19707
19838
  return "";
19708
19839
  try {
19709
19840
  const buffer = readFileSync4(filePath);
@@ -19992,14 +20123,14 @@ async function startWatcher(options) {
19992
20123
  }
19993
20124
  // src/lib/repo-discovery.ts
19994
20125
  init_paths();
19995
- import { existsSync as existsSync13, readFileSync as readFileSync5, readdirSync as readdirSync3, statSync, writeFileSync as writeFileSync5, mkdirSync as mkdirSync10, unlinkSync } from "fs";
20126
+ import { existsSync as existsSync14, readFileSync as readFileSync5, readdirSync as readdirSync3, statSync as statSync2, writeFileSync as writeFileSync4, mkdirSync as mkdirSync11, unlinkSync } from "fs";
19996
20127
  import { createHash } from "crypto";
19997
20128
  import { join as join16, resolve as resolve2, relative as relative2 } from "path";
19998
20129
  function getCacheDir() {
19999
20130
  const testersDir = getTestersDir();
20000
20131
  const cacheDir = join16(testersDir, "repo-index");
20001
- if (!existsSync13(cacheDir)) {
20002
- mkdirSync10(cacheDir, { recursive: true });
20132
+ if (!existsSync14(cacheDir)) {
20133
+ mkdirSync11(cacheDir, { recursive: true });
20003
20134
  }
20004
20135
  return cacheDir;
20005
20136
  }
@@ -20012,10 +20143,10 @@ function getCachePath(repoPath) {
20012
20143
  function isCacheStale(cached, repoPath) {
20013
20144
  for (const spec of cached.specs) {
20014
20145
  const fullPath = join16(repoPath, spec.file);
20015
- if (!existsSync13(fullPath))
20146
+ if (!existsSync14(fullPath))
20016
20147
  return true;
20017
20148
  try {
20018
- const stat = statSync(fullPath);
20149
+ const stat = statSync2(fullPath);
20019
20150
  if (stat.mtimeMs !== spec.mtimeMs)
20020
20151
  return true;
20021
20152
  } catch {
@@ -20024,10 +20155,10 @@ function isCacheStale(cached, repoPath) {
20024
20155
  }
20025
20156
  if (cached.configPath) {
20026
20157
  const configFullPath = join16(repoPath, cached.configPath);
20027
- if (!existsSync13(configFullPath))
20158
+ if (!existsSync14(configFullPath))
20028
20159
  return true;
20029
20160
  try {
20030
- statSync(configFullPath);
20161
+ statSync2(configFullPath);
20031
20162
  const age = Date.now() - new Date(cached.snapshotAt).getTime();
20032
20163
  if (age > 3600000)
20033
20164
  return true;
@@ -20039,7 +20170,7 @@ function isCacheStale(cached, repoPath) {
20039
20170
  }
20040
20171
  function loadCache(repoPath) {
20041
20172
  const cachePath = getCachePath(repoPath);
20042
- if (!existsSync13(cachePath))
20173
+ if (!existsSync14(cachePath))
20043
20174
  return null;
20044
20175
  try {
20045
20176
  const raw = JSON.parse(readFileSync5(cachePath, "utf-8"));
@@ -20050,14 +20181,14 @@ function loadCache(repoPath) {
20050
20181
  }
20051
20182
  function saveCache(snapshot) {
20052
20183
  const cachePath = getCachePath(snapshot.repoPath);
20053
- writeFileSync5(cachePath, JSON.stringify(snapshot, null, 2), "utf-8");
20184
+ writeFileSync4(cachePath, JSON.stringify(snapshot, null, 2), "utf-8");
20054
20185
  }
20055
20186
  function detectPackageManager(repoPath) {
20056
20187
  const result = {
20057
- npm: existsSync13(join16(repoPath, "package-lock.json")),
20058
- yarn: existsSync13(join16(repoPath, "yarn.lock")),
20059
- pnpm: existsSync13(join16(repoPath, "pnpm-lock.yaml")),
20060
- bun: existsSync13(join16(repoPath, "bun.lockb")) || existsSync13(join16(repoPath, "bun.lock")),
20188
+ npm: existsSync14(join16(repoPath, "package-lock.json")),
20189
+ yarn: existsSync14(join16(repoPath, "yarn.lock")),
20190
+ pnpm: existsSync14(join16(repoPath, "pnpm-lock.yaml")),
20191
+ bun: existsSync14(join16(repoPath, "bun.lockb")) || existsSync14(join16(repoPath, "bun.lock")),
20061
20192
  preferred: "npm"
20062
20193
  };
20063
20194
  if (result.bun)
@@ -20072,7 +20203,7 @@ function detectPackageManager(repoPath) {
20072
20203
  }
20073
20204
  function detectDevScripts(repoPath) {
20074
20205
  const pkgPath = join16(repoPath, "package.json");
20075
- if (!existsSync13(pkgPath)) {
20206
+ if (!existsSync14(pkgPath)) {
20076
20207
  return { dev: null, test: null, seed: null, build: null };
20077
20208
  }
20078
20209
  let scripts;
@@ -20098,7 +20229,7 @@ function findPlaywrightConfig(repoPath) {
20098
20229
  "playwright-ct.config.js"
20099
20230
  ];
20100
20231
  for (const name of candidates) {
20101
- if (existsSync13(join16(repoPath, name)))
20232
+ if (existsSync14(join16(repoPath, name)))
20102
20233
  return name;
20103
20234
  }
20104
20235
  return null;
@@ -20157,7 +20288,7 @@ function findSpecFiles(repoPath, globPatterns) {
20157
20288
  const dirsToSearch = ["", ".", "tests", "e2e", "test", "__tests__", "specs", "src"];
20158
20289
  for (const dir of dirsToSearch) {
20159
20290
  const searchDir = dir ? join16(repoPath, dir) : repoPath;
20160
- if (!existsSync13(searchDir))
20291
+ if (!existsSync14(searchDir))
20161
20292
  continue;
20162
20293
  try {
20163
20294
  const files = walkDir(searchDir);
@@ -20169,7 +20300,7 @@ function findSpecFiles(repoPath, globPatterns) {
20169
20300
  seen.add(relativePath);
20170
20301
  const content = readFileSync5(file, "utf-8");
20171
20302
  const contentHash = createHash("sha256").update(content).digest("hex").slice(0, 16);
20172
- const stat = statSync(file);
20303
+ const stat = statSync2(file);
20173
20304
  specs.push({
20174
20305
  file: relativePath,
20175
20306
  fromGlob: pattern,
@@ -20209,7 +20340,7 @@ function matchesGlob(filePath, pattern) {
20209
20340
  }
20210
20341
  function detectSuggestedUrl(repoPath) {
20211
20342
  const pkgPath = join16(repoPath, "package.json");
20212
- if (!existsSync13(pkgPath))
20343
+ if (!existsSync14(pkgPath))
20213
20344
  return null;
20214
20345
  try {
20215
20346
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
@@ -20229,10 +20360,10 @@ function detectSuggestedUrl(repoPath) {
20229
20360
  }
20230
20361
  function checkPlaywrightBrowserInstalled(repoPath) {
20231
20362
  const cacheDir = join16(repoPath, "node_modules", ".cache", "ms-playwright");
20232
- if (existsSync13(cacheDir))
20363
+ if (existsSync14(cacheDir))
20233
20364
  return true;
20234
20365
  const globalCache = join16(repoPath, ".cache", "ms-playwright");
20235
- if (existsSync13(globalCache))
20366
+ if (existsSync14(globalCache))
20236
20367
  return true;
20237
20368
  return false;
20238
20369
  }
@@ -20272,7 +20403,7 @@ function discoverRepo(opts) {
20272
20403
  const specs = findSpecFiles(repoPath, globPatterns);
20273
20404
  const packageManager = detectPackageManager(repoPath);
20274
20405
  const devScripts = detectDevScripts(repoPath);
20275
- const playwrightInstalled = existsSync13(join16(repoPath, "node_modules", "playwright")) || existsSync13(join16(repoPath, "node_modules", "@playwright", "test"));
20406
+ const playwrightInstalled = existsSync14(join16(repoPath, "node_modules", "playwright")) || existsSync14(join16(repoPath, "node_modules", "@playwright", "test"));
20276
20407
  const browsersInstalled = checkPlaywrightBrowserInstalled(repoPath);
20277
20408
  const configExists = configPath !== null;
20278
20409
  const specsFound = specs.length > 0;
@@ -20331,11 +20462,11 @@ function discoverRepo(opts) {
20331
20462
  }
20332
20463
  function clearDiscoveryCache(repoPath) {
20333
20464
  const cacheDir = getCacheDir();
20334
- if (!existsSync13(cacheDir))
20465
+ if (!existsSync14(cacheDir))
20335
20466
  return;
20336
20467
  if (repoPath) {
20337
20468
  const cachePath = getCachePath(repoPath);
20338
- if (existsSync13(cachePath)) {
20469
+ if (existsSync14(cachePath)) {
20339
20470
  unlinkSync(cachePath);
20340
20471
  }
20341
20472
  } else {
@@ -20348,7 +20479,7 @@ function clearDiscoveryCache(repoPath) {
20348
20479
  }
20349
20480
  function getDiscoveryCacheInfo(repoPath) {
20350
20481
  const cachePath = getCachePath(repoPath);
20351
- if (!existsSync13(cachePath))
20482
+ if (!existsSync14(cachePath))
20352
20483
  return null;
20353
20484
  const cached = loadCache(repoPath);
20354
20485
  if (!cached)
@@ -20364,11 +20495,11 @@ init_runs();
20364
20495
  init_database();
20365
20496
  init_paths();
20366
20497
  import { execSync as execSync2 } from "child_process";
20367
- import { existsSync as existsSync14, mkdirSync as mkdirSync11, writeFileSync as writeFileSync6 } from "fs";
20498
+ import { existsSync as existsSync15, mkdirSync as mkdirSync12, writeFileSync as writeFileSync5 } from "fs";
20368
20499
  import { join as join17 } from "path";
20369
20500
  function resolvePlaywrightCmd(repoPath) {
20370
20501
  const localPw = join17(repoPath, "node_modules", ".bin", "playwright");
20371
- if (existsSync14(localPw)) {
20502
+ if (existsSync15(localPw)) {
20372
20503
  return [localPw, "test"];
20373
20504
  }
20374
20505
  return ["npx", "playwright", "test"];
@@ -20564,9 +20695,9 @@ async function runRepoTests(opts) {
20564
20695
  const resultRecord = { id: resultId };
20565
20696
  if (result.stdout || result.stderr) {
20566
20697
  const reportersDir = join17(getTestersDir(), "repo-run-output");
20567
- mkdirSync11(reportersDir, { recursive: true });
20698
+ mkdirSync12(reportersDir, { recursive: true });
20568
20699
  const outputFile = join17(reportersDir, `${resultRecord.id}.log`);
20569
- writeFileSync6(outputFile, `=== stdout ===
20700
+ writeFileSync5(outputFile, `=== stdout ===
20570
20701
  ${result.stdout}
20571
20702
 
20572
20703
  === stderr ===
@@ -14,6 +14,12 @@ export interface WorkflowSandboxPlan {
14
14
  name: string;
15
15
  remoteDir: string;
16
16
  stateRemoteDir: string;
17
+ appSourceDir?: string;
18
+ appRemoteDir?: string;
19
+ appStartCommand?: string;
20
+ appUrl?: string;
21
+ appWaitUrl?: string;
22
+ appWaitTimeoutMs?: number;
17
23
  command: string;
18
24
  cleanup: WorkflowSandboxCleanup;
19
25
  syncStrategy: WorkflowSandboxSyncStrategy;
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-runner.d.ts","sourceRoot":"","sources":["../../src/lib/workflow-runner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workflow-runner.d.ts","sourceRoot":"","sources":["../../src/lib/workflow-runner.ts"],"names":[],"mappings":"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"}