@hasna/testers 0.0.7 → 0.0.8

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/index.js CHANGED
@@ -2795,6 +2795,168 @@ var init_runs = __esm(() => {
2795
2795
  init_database();
2796
2796
  });
2797
2797
 
2798
+ // src/lib/browser-lightpanda.ts
2799
+ var exports_browser_lightpanda = {};
2800
+ __export(exports_browser_lightpanda, {
2801
+ startLightpandaServer: () => startLightpandaServer,
2802
+ launchLightpanda: () => launchLightpanda,
2803
+ isLightpandaAvailable: () => isLightpandaAvailable,
2804
+ installLightpanda: () => installLightpanda,
2805
+ getLightpandaPage: () => getLightpandaPage,
2806
+ closeLightpanda: () => closeLightpanda
2807
+ });
2808
+ import { chromium } from "playwright";
2809
+ import { spawn } from "child_process";
2810
+ function isLightpandaAvailable() {
2811
+ try {
2812
+ const possiblePaths = [
2813
+ `${process.env["HOME"]}/.cache/lightpanda-node/lightpanda`,
2814
+ process.env["LIGHTPANDA_EXECUTABLE_PATH"]
2815
+ ];
2816
+ for (const p of possiblePaths) {
2817
+ if (p) {
2818
+ try {
2819
+ const { existsSync: existsSync2 } = __require("fs");
2820
+ if (existsSync2(p))
2821
+ return true;
2822
+ } catch {
2823
+ continue;
2824
+ }
2825
+ }
2826
+ }
2827
+ const { execSync } = __require("child_process");
2828
+ execSync("lightpanda --version", { stdio: "ignore", timeout: 5000 });
2829
+ return true;
2830
+ } catch {
2831
+ return false;
2832
+ }
2833
+ }
2834
+ function findLightpandaBinary() {
2835
+ const envPath = process.env["LIGHTPANDA_EXECUTABLE_PATH"];
2836
+ if (envPath)
2837
+ return envPath;
2838
+ const cachePath = `${process.env["HOME"]}/.cache/lightpanda-node/lightpanda`;
2839
+ try {
2840
+ const { existsSync: existsSync2 } = __require("fs");
2841
+ if (existsSync2(cachePath))
2842
+ return cachePath;
2843
+ } catch {}
2844
+ return "lightpanda";
2845
+ }
2846
+ async function startLightpandaServer(port) {
2847
+ const binary = findLightpandaBinary();
2848
+ const cdpPort = port ?? 9222 + Math.floor(Math.random() * 1000);
2849
+ return new Promise((resolve, reject) => {
2850
+ const proc = spawn(binary, ["serve", "--port", String(cdpPort)], {
2851
+ stdio: ["ignore", "pipe", "pipe"]
2852
+ });
2853
+ let resolved = false;
2854
+ const timeout = setTimeout(() => {
2855
+ if (!resolved) {
2856
+ resolved = true;
2857
+ resolve({
2858
+ process: proc,
2859
+ wsEndpoint: `ws://127.0.0.1:${cdpPort}`
2860
+ });
2861
+ }
2862
+ }, 5000);
2863
+ proc.stdout?.on("data", (data) => {
2864
+ const output = data.toString();
2865
+ if (output.includes("127.0.0.1") || output.includes("listening") || output.includes("DevTools")) {
2866
+ if (!resolved) {
2867
+ resolved = true;
2868
+ clearTimeout(timeout);
2869
+ resolve({
2870
+ process: proc,
2871
+ wsEndpoint: `ws://127.0.0.1:${cdpPort}`
2872
+ });
2873
+ }
2874
+ }
2875
+ });
2876
+ proc.stderr?.on("data", (data) => {
2877
+ const output = data.toString();
2878
+ if (output.includes("127.0.0.1") || output.includes("listening")) {
2879
+ if (!resolved) {
2880
+ resolved = true;
2881
+ clearTimeout(timeout);
2882
+ resolve({
2883
+ process: proc,
2884
+ wsEndpoint: `ws://127.0.0.1:${cdpPort}`
2885
+ });
2886
+ }
2887
+ }
2888
+ });
2889
+ proc.on("error", (err) => {
2890
+ clearTimeout(timeout);
2891
+ if (!resolved) {
2892
+ resolved = true;
2893
+ reject(new BrowserError(`Failed to start Lightpanda: ${err.message}. ` + `Install it with: bun install @lightpanda/browser`));
2894
+ }
2895
+ });
2896
+ proc.on("exit", (code) => {
2897
+ if (!resolved) {
2898
+ resolved = true;
2899
+ clearTimeout(timeout);
2900
+ reject(new BrowserError(`Lightpanda exited with code ${code}. ` + `Install it with: bun install @lightpanda/browser`));
2901
+ }
2902
+ });
2903
+ });
2904
+ }
2905
+ async function launchLightpanda(_options) {
2906
+ try {
2907
+ const { process: proc, wsEndpoint } = await startLightpandaServer();
2908
+ lightpandaProcess = proc;
2909
+ const browser = await chromium.connectOverCDP(wsEndpoint);
2910
+ return browser;
2911
+ } catch (error) {
2912
+ const message = error instanceof Error ? error.message : String(error);
2913
+ throw new BrowserError(`Failed to launch Lightpanda: ${message}`);
2914
+ }
2915
+ }
2916
+ async function getLightpandaPage(browser, options) {
2917
+ try {
2918
+ const contexts = browser.contexts();
2919
+ const context = contexts.length > 0 ? contexts[0] : await browser.newContext({
2920
+ viewport: options?.viewport ?? { width: 1280, height: 720 },
2921
+ userAgent: options?.userAgent,
2922
+ locale: options?.locale
2923
+ });
2924
+ const page = await context.newPage();
2925
+ return page;
2926
+ } catch (error) {
2927
+ const message = error instanceof Error ? error.message : String(error);
2928
+ throw new BrowserError(`Failed to create Lightpanda page: ${message}`);
2929
+ }
2930
+ }
2931
+ async function closeLightpanda(browser) {
2932
+ try {
2933
+ await browser.close();
2934
+ } catch {}
2935
+ if (lightpandaProcess) {
2936
+ try {
2937
+ lightpandaProcess.kill("SIGTERM");
2938
+ lightpandaProcess = null;
2939
+ } catch {}
2940
+ }
2941
+ }
2942
+ async function installLightpanda() {
2943
+ const { execSync } = __require("child_process");
2944
+ try {
2945
+ execSync("bun install @lightpanda/browser", {
2946
+ stdio: "inherit",
2947
+ cwd: process.env["HOME"]
2948
+ });
2949
+ } catch (error) {
2950
+ const message = error instanceof Error ? error.message : String(error);
2951
+ throw new BrowserError(`Failed to install Lightpanda: ${message}
2952
+ ` + `Try manually: bun install @lightpanda/browser`);
2953
+ }
2954
+ }
2955
+ var lightpandaProcess = null;
2956
+ var init_browser_lightpanda = __esm(() => {
2957
+ init_types();
2958
+ });
2959
+
2798
2960
  // src/db/flows.ts
2799
2961
  var exports_flows = {};
2800
2962
  __export(exports_flows, {
@@ -3050,9 +3212,9 @@ __export(exports_recorder, {
3050
3212
  recordAndSave: () => recordAndSave,
3051
3213
  actionsToScenarioInput: () => actionsToScenarioInput
3052
3214
  });
3053
- import { chromium as chromium2 } from "playwright";
3215
+ import { chromium as chromium3 } from "playwright";
3054
3216
  async function recordSession(url, options) {
3055
- const browser = await chromium2.launch({ headless: false });
3217
+ const browser = await chromium3.launch({ headless: false });
3056
3218
  const context = await browser.newContext({ viewport: { width: 1280, height: 720 } });
3057
3219
  const page = await context.newPage();
3058
3220
  const actions = [];
@@ -3336,14 +3498,22 @@ init_scenarios();
3336
3498
 
3337
3499
  // src/lib/browser.ts
3338
3500
  init_types();
3339
- import { chromium } from "playwright";
3501
+ import { chromium as chromium2 } from "playwright";
3340
3502
  import { execSync } from "child_process";
3341
3503
  var DEFAULT_VIEWPORT = { width: 1280, height: 720 };
3342
3504
  async function launchBrowser(options) {
3505
+ const engine = options?.engine ?? process.env["TESTERS_BROWSER_ENGINE"] ?? "playwright";
3506
+ if (engine === "lightpanda") {
3507
+ const { launchLightpanda: launchLightpanda2, isLightpandaAvailable: isLightpandaAvailable2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
3508
+ if (!isLightpandaAvailable2()) {
3509
+ throw new BrowserError("Lightpanda not installed. Run: testers install-browser --engine lightpanda");
3510
+ }
3511
+ return launchLightpanda2({ viewport: options?.viewport });
3512
+ }
3343
3513
  const headless = options?.headless ?? true;
3344
3514
  const viewport = options?.viewport ?? DEFAULT_VIEWPORT;
3345
3515
  try {
3346
- const browser = await chromium.launch({
3516
+ const browser = await chromium2.launch({
3347
3517
  headless,
3348
3518
  args: [
3349
3519
  `--window-size=${viewport.width},${viewport.height}`
@@ -3356,6 +3526,11 @@ async function launchBrowser(options) {
3356
3526
  }
3357
3527
  }
3358
3528
  async function getPage(browser, options) {
3529
+ const engine = options?.engine ?? "playwright";
3530
+ if (engine === "lightpanda") {
3531
+ const { getLightpandaPage: getLightpandaPage2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
3532
+ return getLightpandaPage2(browser, options);
3533
+ }
3359
3534
  const viewport = options?.viewport ?? DEFAULT_VIEWPORT;
3360
3535
  try {
3361
3536
  const context = await browser.newContext({
@@ -3370,7 +3545,11 @@ async function getPage(browser, options) {
3370
3545
  throw new BrowserError(`Failed to create page: ${message}`);
3371
3546
  }
3372
3547
  }
3373
- async function closeBrowser(browser) {
3548
+ async function closeBrowser(browser, engine) {
3549
+ if (engine === "lightpanda") {
3550
+ const { closeLightpanda: closeLightpanda2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
3551
+ return closeLightpanda2(browser);
3552
+ }
3374
3553
  try {
3375
3554
  await browser.close();
3376
3555
  } catch (error) {
@@ -3378,7 +3557,11 @@ async function closeBrowser(browser) {
3378
3557
  throw new BrowserError(`Failed to close browser: ${message}`);
3379
3558
  }
3380
3559
  }
3381
- async function installBrowser() {
3560
+ async function installBrowser(engine) {
3561
+ if (engine === "lightpanda") {
3562
+ const { installLightpanda: installLightpanda2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
3563
+ return installLightpanda2();
3564
+ }
3382
3565
  try {
3383
3566
  execSync("bunx playwright install chromium", {
3384
3567
  stdio: "inherit"
@@ -4452,7 +4635,7 @@ async function runSingleScenario(scenario, runId, options) {
4452
4635
  let browser = null;
4453
4636
  let page = null;
4454
4637
  try {
4455
- browser = await launchBrowser({ headless: !(options.headed ?? false) });
4638
+ browser = await launchBrowser({ headless: !(options.headed ?? false), engine: options.engine });
4456
4639
  page = await getPage(browser, {
4457
4640
  viewport: config.browser.viewport
4458
4641
  });
@@ -4517,7 +4700,7 @@ async function runSingleScenario(scenario, runId, options) {
4517
4700
  return updatedResult;
4518
4701
  } finally {
4519
4702
  if (browser)
4520
- await closeBrowser(browser);
4703
+ await closeBrowser(browser, options.engine);
4521
4704
  }
4522
4705
  }
4523
4706
  async function runBatch(scenarios, options) {
@@ -6489,7 +6672,7 @@ program2.command("delete <id>").description("Delete a scenario").action((id) =>
6489
6672
  program2.command("run [url] [description]").description("Run test scenarios against a URL").option("-t, --tag <tag>", "Filter by tag (repeatable)", (val, acc) => {
6490
6673
  acc.push(val);
6491
6674
  return acc;
6492
- }, []).option("-s, --scenario <id>", "Run specific scenario ID").option("-p, --priority <level>", "Filter by priority").option("--headed", "Run browser in headed mode", false).option("-m, --model <model>", "AI model to use").option("--parallel <n>", "Number of parallel browsers", "1").option("--json", "Output results as JSON", false).option("-o, --output <filepath>", "Write JSON results to file").option("--timeout <ms>", "Timeout in milliseconds").option("--from-todos", "Import scenarios from todos before running", false).option("--project <id>", "Project ID").option("-b, --background", "Start run in background and return immediately", false).option("--env <name>", "Use a named environment for the URL").action(async (urlArg, description, opts) => {
6675
+ }, []).option("-s, --scenario <id>", "Run specific scenario ID").option("-p, --priority <level>", "Filter by priority").option("--headed", "Run browser in headed mode", false).option("-m, --model <model>", "AI model to use").option("--parallel <n>", "Number of parallel browsers", "1").option("--json", "Output results as JSON", false).option("-o, --output <filepath>", "Write JSON results to file").option("--timeout <ms>", "Timeout in milliseconds").option("--from-todos", "Import scenarios from todos before running", false).option("--project <id>", "Project ID").option("-b, --background", "Start run in background and return immediately", false).option("--browser <engine>", "Browser engine: playwright or lightpanda", "playwright").option("--env <name>", "Use a named environment for the URL").action(async (urlArg, description, opts) => {
6493
6676
  try {
6494
6677
  const projectId = resolveProject(opts.project);
6495
6678
  let url = urlArg;
@@ -6529,7 +6712,8 @@ program2.command("run [url] [description]").description("Run test scenarios agai
6529
6712
  headed: opts.headed,
6530
6713
  parallel: parseInt(opts.parallel, 10),
6531
6714
  timeout: opts.timeout ? parseInt(opts.timeout, 10) : undefined,
6532
- projectId
6715
+ projectId,
6716
+ engine: opts.browser
6533
6717
  });
6534
6718
  console.log(chalk5.green(`Run started in background: ${chalk5.bold(runId.slice(0, 8))}`));
6535
6719
  console.log(chalk5.dim(` Scenarios: ${scenarioCount}`));
@@ -6592,7 +6776,8 @@ program2.command("run [url] [description]").description("Run test scenarios agai
6592
6776
  headed: opts.headed,
6593
6777
  parallel: parseInt(opts.parallel, 10),
6594
6778
  timeout: opts.timeout ? parseInt(opts.timeout, 10) : undefined,
6595
- projectId
6779
+ projectId,
6780
+ engine: opts.browser
6596
6781
  });
6597
6782
  if (opts.json || opts.output) {
6598
6783
  const jsonOutput = formatJSON(run2, results2);
@@ -6617,7 +6802,8 @@ program2.command("run [url] [description]").description("Run test scenarios agai
6617
6802
  headed: opts.headed,
6618
6803
  parallel: parseInt(opts.parallel, 10),
6619
6804
  timeout: opts.timeout ? parseInt(opts.timeout, 10) : undefined,
6620
- projectId
6805
+ projectId,
6806
+ engine: opts.browser
6621
6807
  });
6622
6808
  if (opts.json || opts.output) {
6623
6809
  const jsonOutput = formatJSON(run, results);
@@ -6781,11 +6967,18 @@ program2.command("status").description("Show database and auth status").action((
6781
6967
  process.exit(1);
6782
6968
  }
6783
6969
  });
6784
- program2.command("install-browser").description("Install Playwright Chromium browser").action(async () => {
6970
+ program2.command("install-browser").description("Install browser engine").option("--engine <engine>", "Engine to install: playwright, lightpanda, or all", "playwright").action(async (opts) => {
6785
6971
  try {
6786
- console.log(chalk5.blue("Installing Playwright Chromium..."));
6787
- await installBrowser();
6788
- console.log(chalk5.green("Browser installed successfully."));
6972
+ if (opts.engine === "all" || opts.engine === "playwright") {
6973
+ console.log(chalk5.blue("Installing Playwright Chromium..."));
6974
+ await installBrowser("playwright");
6975
+ console.log(chalk5.green("Playwright Chromium installed."));
6976
+ }
6977
+ if (opts.engine === "all" || opts.engine === "lightpanda") {
6978
+ console.log(chalk5.blue("Installing Lightpanda..."));
6979
+ await installBrowser("lightpanda");
6980
+ console.log(chalk5.green("Lightpanda installed."));
6981
+ }
6789
6982
  } catch (error) {
6790
6983
  console.error(chalk5.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
6791
6984
  process.exit(1);
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { ScenarioPriority, RunStatus, ResultStatus, ModelPreset, ProjectRow, AgentRow, ScenarioRow, RunRow, ResultRow, ScreenshotRow, Project, Agent, Scenario, Run, Result, Screenshot, CreateScenarioInput, UpdateScenarioInput, CreateRunInput, ScenarioFilter, RunFilter, ScheduleRow, Schedule, CreateScheduleInput, UpdateScheduleInput, ScheduleFilter, FlowRow, Flow, CreateFlowInput, AuthConfig, BrowserConfig, ScreenshotConfig, TestersConfig, } from "./types/index.js";
1
+ export type { ScenarioPriority, RunStatus, ResultStatus, ModelPreset, BrowserEngine, ProjectRow, AgentRow, ScenarioRow, RunRow, ResultRow, ScreenshotRow, Project, Agent, Scenario, Run, Result, Screenshot, CreateScenarioInput, UpdateScenarioInput, CreateRunInput, ScenarioFilter, RunFilter, ScheduleRow, Schedule, CreateScheduleInput, UpdateScheduleInput, ScheduleFilter, FlowRow, Flow, CreateFlowInput, AuthConfig, BrowserConfig, ScreenshotConfig, TestersConfig, } from "./types/index.js";
2
2
  export { MODEL_MAP, projectFromRow, agentFromRow, scenarioFromRow, runFromRow, resultFromRow, screenshotFromRow, scheduleFromRow, ScenarioNotFoundError, RunNotFoundError, ResultNotFoundError, VersionConflictError, BrowserError, AIClientError, TodosConnectionError, ProjectNotFoundError, AgentNotFoundError, ScheduleNotFoundError, FlowNotFoundError, DependencyCycleError, flowFromRow, } from "./types/index.js";
3
3
  export { getDatabase, closeDatabase, resetDatabase, resolvePartialId, now, uuid, shortUuid, } from "./db/database.js";
4
4
  export { createScenario, getScenario, getScenarioByShortId, listScenarios, updateScenario, deleteScenario, } from "./db/scenarios.js";
@@ -11,6 +11,7 @@ export { createSchedule, getSchedule, listSchedules, updateSchedule, deleteSched
11
11
  export { addDependency, removeDependency, getDependencies, getDependents, getTransitiveDependencies, topologicalSort, createFlow, getFlow, listFlows, deleteFlow, } from "./db/flows.js";
12
12
  export { loadConfig, resolveModel as resolveModelConfig, getDefaultConfig, } from "./lib/config.js";
13
13
  export { launchBrowser, getPage, closeBrowser, BrowserPool, installBrowser, } from "./lib/browser.js";
14
+ export { isLightpandaAvailable, launchLightpanda, getLightpandaPage, closeLightpanda, installLightpanda, } from "./lib/browser-lightpanda.js";
14
15
  export { Screenshotter, slugify, generateFilename, getScreenshotDir, ensureDir, } from "./lib/screenshotter.js";
15
16
  export { createClient, resolveModel, runAgentLoop, executeTool, BROWSER_TOOLS, } from "./lib/ai-client.js";
16
17
  export { runSingleScenario, runBatch, runByFilter, startRunAsync, onRunEvent, } from "./lib/runner.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ var __export = (target, all) => {
10
10
  });
11
11
  };
12
12
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
13
+ var __require = import.meta.require;
13
14
 
14
15
  // src/types/index.ts
15
16
  function projectFromRow(row) {
@@ -792,6 +793,168 @@ var init_flows = __esm(() => {
792
793
  init_types();
793
794
  });
794
795
 
796
+ // src/lib/browser-lightpanda.ts
797
+ var exports_browser_lightpanda = {};
798
+ __export(exports_browser_lightpanda, {
799
+ startLightpandaServer: () => startLightpandaServer,
800
+ launchLightpanda: () => launchLightpanda,
801
+ isLightpandaAvailable: () => isLightpandaAvailable,
802
+ installLightpanda: () => installLightpanda,
803
+ getLightpandaPage: () => getLightpandaPage,
804
+ closeLightpanda: () => closeLightpanda
805
+ });
806
+ import { chromium } from "playwright";
807
+ import { spawn } from "child_process";
808
+ function isLightpandaAvailable() {
809
+ try {
810
+ const possiblePaths = [
811
+ `${process.env["HOME"]}/.cache/lightpanda-node/lightpanda`,
812
+ process.env["LIGHTPANDA_EXECUTABLE_PATH"]
813
+ ];
814
+ for (const p of possiblePaths) {
815
+ if (p) {
816
+ try {
817
+ const { existsSync: existsSync3 } = __require("fs");
818
+ if (existsSync3(p))
819
+ return true;
820
+ } catch {
821
+ continue;
822
+ }
823
+ }
824
+ }
825
+ const { execSync } = __require("child_process");
826
+ execSync("lightpanda --version", { stdio: "ignore", timeout: 5000 });
827
+ return true;
828
+ } catch {
829
+ return false;
830
+ }
831
+ }
832
+ function findLightpandaBinary() {
833
+ const envPath = process.env["LIGHTPANDA_EXECUTABLE_PATH"];
834
+ if (envPath)
835
+ return envPath;
836
+ const cachePath = `${process.env["HOME"]}/.cache/lightpanda-node/lightpanda`;
837
+ try {
838
+ const { existsSync: existsSync3 } = __require("fs");
839
+ if (existsSync3(cachePath))
840
+ return cachePath;
841
+ } catch {}
842
+ return "lightpanda";
843
+ }
844
+ async function startLightpandaServer(port) {
845
+ const binary = findLightpandaBinary();
846
+ const cdpPort = port ?? 9222 + Math.floor(Math.random() * 1000);
847
+ return new Promise((resolve, reject) => {
848
+ const proc = spawn(binary, ["serve", "--port", String(cdpPort)], {
849
+ stdio: ["ignore", "pipe", "pipe"]
850
+ });
851
+ let resolved = false;
852
+ const timeout = setTimeout(() => {
853
+ if (!resolved) {
854
+ resolved = true;
855
+ resolve({
856
+ process: proc,
857
+ wsEndpoint: `ws://127.0.0.1:${cdpPort}`
858
+ });
859
+ }
860
+ }, 5000);
861
+ proc.stdout?.on("data", (data) => {
862
+ const output = data.toString();
863
+ if (output.includes("127.0.0.1") || output.includes("listening") || output.includes("DevTools")) {
864
+ if (!resolved) {
865
+ resolved = true;
866
+ clearTimeout(timeout);
867
+ resolve({
868
+ process: proc,
869
+ wsEndpoint: `ws://127.0.0.1:${cdpPort}`
870
+ });
871
+ }
872
+ }
873
+ });
874
+ proc.stderr?.on("data", (data) => {
875
+ const output = data.toString();
876
+ if (output.includes("127.0.0.1") || output.includes("listening")) {
877
+ if (!resolved) {
878
+ resolved = true;
879
+ clearTimeout(timeout);
880
+ resolve({
881
+ process: proc,
882
+ wsEndpoint: `ws://127.0.0.1:${cdpPort}`
883
+ });
884
+ }
885
+ }
886
+ });
887
+ proc.on("error", (err) => {
888
+ clearTimeout(timeout);
889
+ if (!resolved) {
890
+ resolved = true;
891
+ reject(new BrowserError(`Failed to start Lightpanda: ${err.message}. ` + `Install it with: bun install @lightpanda/browser`));
892
+ }
893
+ });
894
+ proc.on("exit", (code) => {
895
+ if (!resolved) {
896
+ resolved = true;
897
+ clearTimeout(timeout);
898
+ reject(new BrowserError(`Lightpanda exited with code ${code}. ` + `Install it with: bun install @lightpanda/browser`));
899
+ }
900
+ });
901
+ });
902
+ }
903
+ async function launchLightpanda(_options) {
904
+ try {
905
+ const { process: proc, wsEndpoint } = await startLightpandaServer();
906
+ lightpandaProcess = proc;
907
+ const browser = await chromium.connectOverCDP(wsEndpoint);
908
+ return browser;
909
+ } catch (error) {
910
+ const message = error instanceof Error ? error.message : String(error);
911
+ throw new BrowserError(`Failed to launch Lightpanda: ${message}`);
912
+ }
913
+ }
914
+ async function getLightpandaPage(browser, options) {
915
+ try {
916
+ const contexts = browser.contexts();
917
+ const context = contexts.length > 0 ? contexts[0] : await browser.newContext({
918
+ viewport: options?.viewport ?? { width: 1280, height: 720 },
919
+ userAgent: options?.userAgent,
920
+ locale: options?.locale
921
+ });
922
+ const page = await context.newPage();
923
+ return page;
924
+ } catch (error) {
925
+ const message = error instanceof Error ? error.message : String(error);
926
+ throw new BrowserError(`Failed to create Lightpanda page: ${message}`);
927
+ }
928
+ }
929
+ async function closeLightpanda(browser) {
930
+ try {
931
+ await browser.close();
932
+ } catch {}
933
+ if (lightpandaProcess) {
934
+ try {
935
+ lightpandaProcess.kill("SIGTERM");
936
+ lightpandaProcess = null;
937
+ } catch {}
938
+ }
939
+ }
940
+ async function installLightpanda() {
941
+ const { execSync } = __require("child_process");
942
+ try {
943
+ execSync("bun install @lightpanda/browser", {
944
+ stdio: "inherit",
945
+ cwd: process.env["HOME"]
946
+ });
947
+ } catch (error) {
948
+ const message = error instanceof Error ? error.message : String(error);
949
+ throw new BrowserError(`Failed to install Lightpanda: ${message}
950
+ ` + `Try manually: bun install @lightpanda/browser`);
951
+ }
952
+ }
953
+ var lightpandaProcess = null;
954
+ var init_browser_lightpanda = __esm(() => {
955
+ init_types();
956
+ });
957
+
795
958
  // src/index.ts
796
959
  init_types();
797
960
  init_database();
@@ -1337,14 +1500,22 @@ function resolveModel(nameOrId) {
1337
1500
  }
1338
1501
  // src/lib/browser.ts
1339
1502
  init_types();
1340
- import { chromium } from "playwright";
1503
+ import { chromium as chromium2 } from "playwright";
1341
1504
  import { execSync } from "child_process";
1342
1505
  var DEFAULT_VIEWPORT = { width: 1280, height: 720 };
1343
1506
  async function launchBrowser(options) {
1507
+ const engine = options?.engine ?? process.env["TESTERS_BROWSER_ENGINE"] ?? "playwright";
1508
+ if (engine === "lightpanda") {
1509
+ const { launchLightpanda: launchLightpanda2, isLightpandaAvailable: isLightpandaAvailable2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
1510
+ if (!isLightpandaAvailable2()) {
1511
+ throw new BrowserError("Lightpanda not installed. Run: testers install-browser --engine lightpanda");
1512
+ }
1513
+ return launchLightpanda2({ viewport: options?.viewport });
1514
+ }
1344
1515
  const headless = options?.headless ?? true;
1345
1516
  const viewport = options?.viewport ?? DEFAULT_VIEWPORT;
1346
1517
  try {
1347
- const browser = await chromium.launch({
1518
+ const browser = await chromium2.launch({
1348
1519
  headless,
1349
1520
  args: [
1350
1521
  `--window-size=${viewport.width},${viewport.height}`
@@ -1357,6 +1528,11 @@ async function launchBrowser(options) {
1357
1528
  }
1358
1529
  }
1359
1530
  async function getPage(browser, options) {
1531
+ const engine = options?.engine ?? "playwright";
1532
+ if (engine === "lightpanda") {
1533
+ const { getLightpandaPage: getLightpandaPage2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
1534
+ return getLightpandaPage2(browser, options);
1535
+ }
1360
1536
  const viewport = options?.viewport ?? DEFAULT_VIEWPORT;
1361
1537
  try {
1362
1538
  const context = await browser.newContext({
@@ -1371,7 +1547,11 @@ async function getPage(browser, options) {
1371
1547
  throw new BrowserError(`Failed to create page: ${message}`);
1372
1548
  }
1373
1549
  }
1374
- async function closeBrowser(browser) {
1550
+ async function closeBrowser(browser, engine) {
1551
+ if (engine === "lightpanda") {
1552
+ const { closeLightpanda: closeLightpanda2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
1553
+ return closeLightpanda2(browser);
1554
+ }
1375
1555
  try {
1376
1556
  await browser.close();
1377
1557
  } catch (error) {
@@ -1385,26 +1565,29 @@ class BrowserPool {
1385
1565
  maxSize;
1386
1566
  headless;
1387
1567
  viewport;
1568
+ engine;
1388
1569
  constructor(size, options) {
1389
1570
  this.maxSize = size;
1390
1571
  this.headless = options?.headless ?? true;
1391
1572
  this.viewport = options?.viewport ?? DEFAULT_VIEWPORT;
1573
+ this.engine = options?.engine ?? "playwright";
1392
1574
  }
1393
1575
  async acquire() {
1394
1576
  const idle = this.pool.find((entry) => !entry.inUse);
1395
1577
  if (idle) {
1396
1578
  idle.inUse = true;
1397
- const page = await getPage(idle.browser, { viewport: this.viewport });
1579
+ const page = await getPage(idle.browser, { viewport: this.viewport, engine: this.engine });
1398
1580
  return { browser: idle.browser, page };
1399
1581
  }
1400
1582
  if (this.pool.length < this.maxSize) {
1401
1583
  const browser = await launchBrowser({
1402
1584
  headless: this.headless,
1403
- viewport: this.viewport
1585
+ viewport: this.viewport,
1586
+ engine: this.engine
1404
1587
  });
1405
1588
  const entry = { browser, inUse: true };
1406
1589
  this.pool.push(entry);
1407
- const page = await getPage(browser, { viewport: this.viewport });
1590
+ const page = await getPage(browser, { viewport: this.viewport, engine: this.engine });
1408
1591
  return { browser, page };
1409
1592
  }
1410
1593
  return new Promise((resolve, reject) => {
@@ -1413,7 +1596,7 @@ class BrowserPool {
1413
1596
  if (available) {
1414
1597
  clearInterval(interval);
1415
1598
  available.inUse = true;
1416
- getPage(available.browser, { viewport: this.viewport }).then((page) => resolve({ browser: available.browser, page })).catch(reject);
1599
+ getPage(available.browser, { viewport: this.viewport, engine: this.engine }).then((page) => resolve({ browser: available.browser, page })).catch(reject);
1417
1600
  }
1418
1601
  }, 50);
1419
1602
  });
@@ -1430,7 +1613,11 @@ class BrowserPool {
1430
1613
  this.pool.length = 0;
1431
1614
  }
1432
1615
  }
1433
- async function installBrowser() {
1616
+ async function installBrowser(engine) {
1617
+ if (engine === "lightpanda") {
1618
+ const { installLightpanda: installLightpanda2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
1619
+ return installLightpanda2();
1620
+ }
1434
1621
  try {
1435
1622
  execSync("bunx playwright install chromium", {
1436
1623
  stdio: "inherit"
@@ -1440,6 +1627,10 @@ async function installBrowser() {
1440
1627
  throw new BrowserError(`Failed to install browser: ${message}`);
1441
1628
  }
1442
1629
  }
1630
+
1631
+ // src/index.ts
1632
+ init_browser_lightpanda();
1633
+
1443
1634
  // src/lib/screenshotter.ts
1444
1635
  import { mkdirSync as mkdirSync2, existsSync as existsSync3, writeFileSync } from "fs";
1445
1636
  import { join as join3 } from "path";
@@ -2514,7 +2705,7 @@ async function runSingleScenario(scenario, runId, options) {
2514
2705
  let browser = null;
2515
2706
  let page = null;
2516
2707
  try {
2517
- browser = await launchBrowser({ headless: !(options.headed ?? false) });
2708
+ browser = await launchBrowser({ headless: !(options.headed ?? false), engine: options.engine });
2518
2709
  page = await getPage(browser, {
2519
2710
  viewport: config.browser.viewport
2520
2711
  });
@@ -2579,7 +2770,7 @@ async function runSingleScenario(scenario, runId, options) {
2579
2770
  return updatedResult;
2580
2771
  } finally {
2581
2772
  if (browser)
2582
- await closeBrowser(browser);
2773
+ await closeBrowser(browser, options.engine);
2583
2774
  }
2584
2775
  }
2585
2776
  async function runBatch(scenarios, options) {
@@ -4861,7 +5052,10 @@ export {
4861
5052
  listFlows,
4862
5053
  listAuthPresets,
4863
5054
  listAgents,
5055
+ launchLightpanda,
4864
5056
  launchBrowser,
5057
+ isLightpandaAvailable,
5058
+ installLightpanda,
4865
5059
  installBrowser,
4866
5060
  initProject,
4867
5061
  importFromTodos,
@@ -4883,6 +5077,7 @@ export {
4883
5077
  getProject,
4884
5078
  getPage,
4885
5079
  getNextRunTime,
5080
+ getLightpandaPage,
4886
5081
  getFlow,
4887
5082
  getExitCode,
4888
5083
  getEnabledSchedules,
@@ -4932,6 +5127,7 @@ export {
4932
5127
  createClient,
4933
5128
  createAuthPreset,
4934
5129
  connectToTodos,
5130
+ closeLightpanda,
4935
5131
  closeDatabase,
4936
5132
  closeBrowser,
4937
5133
  checkBudget,